nest笔记九:参数校验使用延伸
要求专入参数是DTO已经定义的
- 默认的呢况下,参数校验只针对DTO中已经存在的参数,进行校验。而未在DTO中定义的属性,则会被忽略,不去校验。
- 如果要校验参数,只能是DTO的属性,我们只要增加校验选项就可以了。
whitelist 白名单
- 如果设置为 true,validator 将删除任何没有任何装饰器的属性的已验证对象。如果没有合适的装饰器,则用@Alloc这个装饰器。
- 示例:DTO的定义
@ApiTags('这是一个sample')
class DTOSample {
@IsInt()
id: number
@IsString()
name: string
}
这个时候如果传参是
{
"id":1,
"name": "sample name",
"age": 18
}
{
id: 1,
name: 'sample name'
};
forbidNonWhitelisted 禁用非白名单的
- 如果设置为 true,则非白名单属性会被检查出错误。
- 如上例,一个age则返回属age是一个非必要的属错误。
- 一般情况下,我会要求将不必要的参数都检查出来。减少不要的错误
nest使用校验管道的具体实现
import { Injectable, PipeTransform, ArgumentMetadata, ValidationError } from '@nestjs/common';
import { plainToClass } from 'class-transformer';
import { validate } from 'class-validator';
import { EnumErrorCode } from '../error/error_code';
@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
在启动的时候,设置全局校验管道
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}...`);
}
对象中的数据校验
- 如果某个属性是某个对象,并且对象中的属性也是需要校验的话,就需要使用装饰器 @ValidateNested, 如下例
class SampleDetail {
@IsInt()
public id: number;
@IsString()
public name: string;
}
class Sample {
@IsString()
public caption: string;
@ValidateNested()
public detail: SampleDetail;
}
- 结果并没有对detail的值进行校验,原因是类型中,我们是知道detail是SampleDetail类型,但通过参数传入后,它的原型并不是SampleDetail,这样就不能对detail的属性校验。翻了一下官方说明后,原来,使用ValidateNested后,还要对它类型转换。使用Type装饰器。然后,就可以生效了。如下class Sample的代码
class Sample {
@IsString()
public caption: string;
@ValidateNested()
@Type(()=>SampleDetail)
public detail: SampleDetail;
}
数组中,基本数据类型校验
- 对于基本类型的数组,如整数,字符串等。如果仅用默认参数,发现它不并会检查每个元素,只是把整个数字对象拿去当基本类型校验,结果肯定是报错的。
- 翻看官方文档后,发现基本数据类型校验装饰器还有一个参数each?: boolean,要把它设为true, 就可以了。下如示例:
class Sample {
@IsString()
public caption: string;
@ValidateNested()
@Type(()=>SampleDetail)
public detail: SampleDetail;
@IsArray()
@IsInt({each: true})
public ages: number[];
}