NestJS中多DTO接收Query参数时属性重复如何分离
问题原因
NestJS默认行为下:
- 不带参数的
@Query()装饰器会直接返回完整的请求query对象,不会做任何拆分 - 默认启用的
ValidationPipe仅做参数格式校验,不会自动剔除DTO定义外的多余属性,也不会默认把普通对象转换为DTO类的实例
因此两个DTO参数都会拿到全量的请求参数,不会自动按类定义隔离属性。
解决方案
第一步:配置全局ValidationPipe开启自动转换和白名单过滤
在项目入口main.ts中修改全局管道配置,其中transform: true和whitelist: true是核心必填配置,缺一则无法实现属性自动隔离:
import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@nestjs/common'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes( new ValidationPipe({ transform: true, // 自动将普通query/payload/param对象转换为对应DTO类的实例 whitelist: true, // 自动移除DTO类中未声明校验规则的属性 // 可选配置:如果希望传入DTO定义外的参数时直接抛出400错误,可以开启以下配置 // forbidNonWhitelisted: true, }) ); await app.listen(3000); } bootstrap();
配置生效后,class-validator在实例化DTO时,会自动把没有添加任何校验装饰器的属性从实例中剔除:
- 实例化
PageDto时,status、source属性没有在PageDto类中定义校验规则,会被自动过滤,最终实例仅保留page、limit属性 - 实例化
MyDto时,page、limit属性没有在MyDto类中定义校验规则,会被自动过滤,最终实例仅保留status、source属性
注意事项
- DTO中所有需要保留的属性必须添加至少一个class-validator的校验装饰器(比如当前代码里的
@IsInt()、@IsString()),如果某个属性不需要做格式校验,要添加@Allow()装饰器标记,否则会被白名单规则过滤掉 - 如果不想配置全局管道,也可以在对应控制器方法上通过
@UsePipes(new ValidationPipe({transform: true, whitelist: true}))单独给接口加配置,效果一致。
可选替代方案
如果觉得多次实例化DTO不够优雅,可以定义一个聚合的请求DTO,通过@ValidateNested()组合两个子DTO,控制器只需要注入一次即可:
import { Type, ValidateNested } from 'class-validator'; export class QueryDto { @ValidateNested() @Type(() => PageDto) pagination: PageDto; @ValidateNested() @Type(() => MyDto) filter: MyDto; }
这种方式要求请求query传参格式改成嵌套结构,比如?pagination[page]=1&pagination[limit]=10&filter[status]=active&filter[source]=firebase,如果要保持平级的query传参格式,用前面的白名单过滤方案即可。
内容的提问来源于stack exchange,提问作者MarkhorKiller
相关产品推荐
相关产品推荐

