• nest笔记九:参数校验使用延伸


    nest笔记九:参数校验使用延伸

    要求专入参数是DTO已经定义的

    • 默认的呢况下,参数校验只针对DTO中已经存在的参数,进行校验。而未在DTO中定义的属性,则会被忽略,不去校验。
    • 如果要校验参数,只能是DTO的属性,我们只要增加校验选项就可以了。

    whitelist 白名单

    • 如果设置为 true,validator 将删除任何没有任何装饰器的属性的已验证对象。如果没有合适的装饰器,则用@Alloc这个装饰器。
    • 示例:DTO的定义
        @ApiTags('这是一个sample')
        class DTOSample {
            @IsInt()
            id: number
    
            @IsString()
            name: string
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 传入的参数
        这个时候如果传参是
        {
            "id":1,
            "name": "sample name",
            "age": 18
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 最终的结果
    {
        id: 1,
        name: 'sample name'
    };
        // age这个属性没有了
    
    • 1
    • 2
    • 3
    • 4
    • 5

    forbidNonWhitelisted 禁用非白名单的

    • 如果设置为 true,则非白名单属性会被检查出错误。
    • 如上例,一个age则返回属age是一个非必要的属错误。
    • 一般情况下,我会要求将不必要的参数都检查出来。减少不要的错误

    nest使用校验管道的具体实现

    // validation_pipe.ts
    import { Injectable, PipeTransform, ArgumentMetadata, ValidationError } from '@nestjs/common';
    import { plainToClass } from 'class-transformer';
    import { validate } from 'class-validator';
    import { EnumErrorCode } from '../error/error_code';
    
    /**
     * 这是一个全局的参数验证管道,基于class-transformer
     * 如果失败,则会抛出Error
     * 在main.ts的nestApp要将它设为全局的
     */
    
    @Injectable()
    export class ValidationPipe implements PipeTransform {
        async transform(paramValue: any, { metatype: paramMetatype }: ArgumentMetadata) {
            if (!paramMetatype || !this.toValidate(paramMetatype)) {
                return paramValue;
            }
            const object = plainToClass(paramMetatype, paramValue);
            const errors = await validate(object, {
                whitelist: true, // 白名单选项
                forbidNonWhitelisted: true, // 禁用非白名单属性选项
                stopAtFirstError: true, // 碰到第一个错误就返回
                forbidUnknownValues: true, // 禁用未知的值
            });
            const errorList: string[] = [];
            const errObjList: ValidationError[] = [...errors];
    
            do {
                const e = errObjList.shift();
                if (!e) {
                    break;
                }
                if (e.constraints) {
                    for (const item in e.constraints) {
                        if (item === 'whitelistValidation') {
                            // 如果是白名单校验错误的,使用自定义的错误语句描述。
                            errorList.push(`属性 ${e.property} 未定义!`);
                        } else {
                            errorList.push(e.constraints[item]);
                        }
                    }
                }
                if (e.children) {
                    errObjList.push(...e.children);
                }
            } while (true);
            if (errorList.length > 0) {
                throw new Error(`Error:请求参数校验错误: ${errorList.join()}`);
            }
            return object;
        }
    
        private toValidate(paramMetatype: Function): boolean {
            const types: Function[] = [String, Boolean, Number, Array, Object];
            return !types.includes(paramMetatype);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    在启动的时候,设置全局校验管道

    // 在main.ts中的bootstrap函数中
    async function bootstrap() {
        const globalConfig = XConfigUtils.getConfig();
    
        app.enableCors({
            origin: true,
            methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
            credentials: true,
        });
        app.useGlobalPipes(new ValidationPipe());  // 这里设置全局管道
        await app.listen(globalConfig.port as number);
        log.info(`开始侦听:${globalConfig.port}...`);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    对象中的数据校验

    • 如果某个属性是某个对象,并且对象中的属性也是需要校验的话,就需要使用装饰器 @ValidateNested, 如下例
    class SampleDetail {
        @IsInt()
        public id: number;
        @IsString()
        public name: string;
    }
    
    class Sample {
        @IsString()
        public caption: string;
        @ValidateNested()
        public detail: SampleDetail;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 结果并没有对detail的值进行校验,原因是类型中,我们是知道detail是SampleDetail类型,但通过参数传入后,它的原型并不是SampleDetail,这样就不能对detail的属性校验。翻了一下官方说明后,原来,使用ValidateNested后,还要对它类型转换。使用Type装饰器。然后,就可以生效了。如下class Sample的代码
    class Sample {
        @IsString()
        public caption: string;
        @ValidateNested()
        @Type(()=>SampleDetail)   //在这里加装饰器,就解决了。
        public detail: SampleDetail;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    数组中,基本数据类型校验

    • 对于基本类型的数组,如整数,字符串等。如果仅用默认参数,发现它不并会检查每个元素,只是把整个数字对象拿去当基本类型校验,结果肯定是报错的。
    • 翻看官方文档后,发现基本数据类型校验装饰器还有一个参数each?: boolean,要把它设为true, 就可以了。下如示例:
    class Sample {
        @IsString()
        public caption: string;
        @ValidateNested()
        @Type(()=>SampleDetail)
        public detail: SampleDetail;
    
        @IsArray()
        @IsInt({each: true})  //  使用each选项设为true以后,就可以检查数组中每个元素了
        public ages: number[];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    Java私活500元,做个JavaWeb仓储管理网站(二)
    05-5.4.2 树、森林和二叉树的转换
    代码中大量的套娃式ifelse优化方案
    【Typescript重点】泛型的使用
    聊聊Linux中CPU上下文切换
    ROS2自学笔记:机器视觉基础
    电影太大怎么压缩变小?
    深度学习论文翻译解析(二十一):High-Performance Large-Scale Image Recognition Without Normalization
    Linux【vim】
    Flutter旋转平移缩放动画实例 -- 手动实现底部FloatingActionButton弹入弹出动画
  • 原文地址:https://blog.csdn.net/zdhsoft/article/details/126595070