NestJS如何校验POST请求体类型,拦截不符合DTO结构的非法请求
你遇到的空对象问题是两个核心原因导致的:
- 嵌套类型没有用
@Type装饰器标记,class-transformer无法将请求体的普通对象转换为对应类实例,ValidationPipe无法识别属性 - 嵌套层级的所有可选属性没有加
@IsOptional()装饰器,非类类型的interface在运行时会被擦除,校验器无法识别
第一步:确认依赖安装
npm install class-validator class-transformer
第二步:改写DTO定义
所有嵌套结构必须定义为Class(不能用Interface,否则运行时会丢失类型信息),每层属性按需添加校验装饰:
import { IsNumber, IsString, IsOptional, ValidateNested, IsNotEmptyObject } from 'class-validator'; import { Type } from 'class-transformer'; // 替换之前的IDValue interface为Class export class IDValue { @IsString() @IsOptional() ID: string; @IsString() @IsOptional() Value: string; } export class DeliveryInfo { @IsString() @IsOptional() ActualDeliveryDateTime: string; @IsString() @IsOptional() ScheduledDeliveryDateTime: string; } export class SalesOrderGeneralInfo { @IsOptional() @ValidateNested() @Type(() => IDValue) Branch: IDValue; @IsOptional() @ValidateNested() @Type(() => IDValue) InventoryLocation: IDValue; @IsOptional() @ValidateNested() @Type(() => IDValue) SalesOrderClassification: IDValue; @IsString() @IsOptional() Reference: string; } export class SalesOrderUpdate { @IsOptional() @ValidateNested() @Type(() => DeliveryInfo) DeliveryInfo: DeliveryInfo; @IsOptional() @ValidateNested() @Type(() => SalesOrderGeneralInfo) SalesOrderGeneralInfo: SalesOrderGeneralInfo; } export class SalesOrderUpdateDto { @IsNumber() ID: number; @IsNotEmptyObject() @ValidateNested() @Type(() => SalesOrderUpdate) SalesOrder: SalesOrderUpdate; }
第三步:调整ValidationPipe配置
添加forbidNonWhitelisted: true参数,只要传入不在DTO定义中的属性就直接抛出400错误,完全满足拦截非法属性的需求:
@Post('/update') @UsePipes(new ValidationPipe({ whitelist: true, // 自动剥离不在DTO定义中的白名单属性 forbidNonWhitelisted: true, // 存在非法属性时直接返回校验失败 transform: true // 自动将请求体转换为DTO类实例 })) async salesOrderUpdate( @Body() body: SalesOrderUpdateDto, ) { return await this.salesOrderService.updateSalesOrder(`${body.ID}`, body.SalesOrder); }
补充说明
你提到的"指定TS类型后传入不匹配结构也不报错"是TS的运行机制导致的:TS的类型校验只在编译阶段生效,运行时所有类型都会被擦除,NestJS本身不会做运行时的类型校验,必须依赖class-validator这类运行时校验库实现。
以上配置可以完全实现你的需求:
- 允许传入任意子集的合法属性,哪怕只传
SalesOrderGeneralInfo.Branch.Value也可以正常解析 - 只要传入
SalesOrderInsuranceInfo或者其他自定义非法属性,接口会直接返回400校验失败
内容的提问来源于stack exchange,提问作者chxpel
相关产品推荐
相关产品推荐

