在NestJS Service中使用class-validator时校验结果始终为空数组
问题原因
核心问题是:传入Service的user是普通JavaScript对象,而非CreateUserDto的类实例。class-validator的校验装饰器(如@IsEmail())依赖类的元数据工作,普通对象没有这些元数据,导致validate方法无法识别校验规则,返回空数组。
而Controller层校验正常,是因为Nest的ValidationPipe会自动将请求体转换为对应DTO类的实例,并基于元数据执行校验。
解决方案
方案1:让Controller正确传递DTO实例到Service
修改Controller代码,给@Body()添加ValidationPipe并指定参数类型为CreateUserDto,Nest会自动将请求体转换为DTO实例:
@Post() async create(@Body(new ValidationPipe()) user: CreateUserDto) { return this.userService.create(user); }
此时Service接收的user已是CreateUserDto实例,直接调用校验即可:
import { validate } from 'class-validator'; async create(user: CreateUserDto): Promise<UserEntity> { const errors = await validate(user); if (errors.length > 0) { throw new Error('Invalid criteria!'); } // 密码加密、创建逻辑... }
若项目全局启用了ValidationPipe,可省略
new ValidationPipe(),直接写@Body() user: CreateUserDto。
方案2:在Service中手动转换普通对象为DTO实例
如果不想修改Controller,可在Service内部用class-transformer的plainToInstance方法转换对象后再校验:
先安装依赖:
npm install class-transformer
修改Service代码:
import { validate } from 'class-validator'; import { plainToInstance } from 'class-transformer'; export class UserService extends BaseService<UserEntity> { constructor( @Inject('USER_RESPOSITORY') private userRespository: typeof UserEntity ) { super(userRespository); } async create(user: CreateUserDto): Promise<UserEntity> { // 将普通对象转换为DTO实例 const userDto = plainToInstance(CreateUserDto, user); const errors = await validate(userDto); if (errors.length > 0) { throw new Error('Invalid criteria!'); } const hashedPassword = await bcrypt.hash(userDto.password, 10); userDto.password = hashedPassword; const rs = await super.create(userDto); return rs; } }
无需手动实例化
Validator,直接使用class-validator导出的validate函数即可,这是当前推荐用法。
额外说明
- Controller层校验正常的本质:ValidationPipe内部自动执行
plainToInstance转换,再调用validate校验,因此能识别DTO的校验规则。 - 避免手动实例化
Validator:class-validator从v0.13.x开始推荐使用函数式的validate、validateOrReject等方法,而非new Validator()实例。
内容的提问来源于stack exchange,提问作者Han Luu Nhat
相关产品推荐
相关产品推荐

