NestJS中如何设置class-validator与class-transformer装饰器执行顺序
问题结论:在NestJS中结合使用class-validator和class-transformer时,可以控制装饰器的执行顺序,你遇到的报错是默认执行逻辑不符合预期导致的。
报错原因
默认的执行顺序规则如下:
- 所有class-transformer的
@Transform转换装饰器,执行优先级高于class-validator的所有校验装饰器,也就是说会先跑完所有转换逻辑,才会触发校验逻辑 - 同类型的装饰器(比如同为
@Transform或者同为校验装饰器),执行顺序为从下到上,离属性声明越近的装饰器先执行
你传入foo: null时,还没轮到@IsString校验,就先执行了@Transform里的validator.trim(value),此时value为null,调用方法直接抛出类型错误。
解决方案
方案1:给转换逻辑增加类型判断(最简便)
在@Transform的回调中先判断值类型是否符合要求,避免空值/非字符串值调用方法报错:
@IsOptional() @IsString() @IsByteLength(1, 2048) @Transform(({ value }) => typeof value === 'string' ? validator.trim(value) : value) @Transform(({ value }) => typeof value === 'string' ? validator.stripLow(value) : value) foo: string;
修改后传入null时,转换逻辑不会报错,后续@IsString会正常抛出Expected a string but received a null的校验错误,符合预期。
方案2:调整同类型装饰器的执行顺序
如果需要调整转换逻辑或者校验逻辑的先后执行顺序,直接调整装饰器的上下位置即可,比如要先执行trim再执行stripLow,调整成如下顺序即可:
@Transform(({ value }) => typeof value === 'string' ? validator.stripLow(value) : value) @Transform(({ value }) => typeof value === 'string' ? validator.trim(value) : value) foo: string;
方案3:自定义Pipe实现先校验后转换
如果需要彻底调整转换和校验的全局顺序,让所有校验逻辑先于转换执行,可以自定义ValidationPipe:
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common'; import { validate } from 'class-validator'; import { plainToInstance } from 'class-transformer'; @Injectable() export class CustomValidationPipe implements PipeTransform<any> { async transform(value: any, { metatype }: ArgumentMetadata) { if (!metatype || [String, Boolean, Number, Array, Object].includes(metatype as any)) { return value; } // 先校验原始值 const preCheckInstance = plainToInstance(metatype, value, { ignoreDecorators: true }); const preErrors = await validate(preCheckInstance); if (preErrors.length) { throw new BadRequestException('参数校验失败'); } // 校验通过后执行转换和二次校验 const instance = plainToInstance(metatype, value); const finalErrors = await validate(instance); if (finalErrors.length) { throw new BadRequestException('参数校验失败'); } return instance; } }
之后在main.ts中替换默认的ValidationPipe即可。
内容的提问来源于stack exchange,提问作者rand0rn
相关产品推荐
相关产品推荐

