NestJS中如何验证d_from[abc]这类嵌套日期范围查询参数
NestJS实现日期字段的嵌套日期范围查询参数验证
问题背景
需要在NestJS中支持针对多个日期字段的范围查询,每次请求仅使用其中一个日期字段的范围(d_from和d_to)。现有DTO能正常验证普通查询参数,但嵌套的日期范围参数无法正确解析和验证,请求时触发白名单校验错误。
现有代码
CustomerPaymentQueryParamDto
export class CustomerPaymentQueryParamDto extends RequestQueryModel { @Transform(({ value: it }: { value: string }) => Number.parseInt(it)) @IsOptional() @Min(1) public readonly page_number?: number @Transform(({ value: it }: { value: string }) => Number.parseInt(it)) @IsOptional() @Min(1) public readonly page_size?: number @IsOptional() @IsString() public readonly filter?: string @IsOptional() @IsString() public readonly sortBy?: string @Transform(({ value: it }: { value: string }) => (isString(it) && ['true', 'false'].includes(it.toLowerCase()) ? it.toLowerCase() === 'true' : it)) @IsOptional() @IsBoolean() public readonly descending?: boolean @IsOptional() @Type(() => DateFromTo) @ValidateNested() public readonly d_from?: DateFromTo @IsOptional() @Type(() => DateFromTo) @ValidateNested() public readonly d_to?: DateFromTo // ...其他字段 }
DateFromTo类
import { IsOptional, IsString } from 'class-validator' export class DateFromTo { @IsString() @IsOptional() payment_date?: string @IsString() @IsOptional() create_date?: string public constructor(paymentDate: string | undefined, createDate: string | undefined) { this.payment_date = paymentDate this.create_date = createDate } }
请求示例
GET /payments?d_from[payment_date]=2018-08-01&d_to[payment_date]=2018-08-03&page_number=1&page_size=10
错误信息
请求后返回400错误:
{ "code": { "key": "validation.invalid_query_parameter", "message": "The value for the query parameter 'd_from[payment_date]' is invalid", "httpStatus": 400 } }
Validation Pipe打印的详细错误:
value:{"d_from[payment_date]":"2018-08-01","d_to[payment_date]":"2018-08-03","page_number":"1","page_size":"10"}, meta: undefined, val: {"forbidUnknownValues":true,"whitelist":true,"forbidNonWhitelisted":true,"validationError":{"target":true}}, entity: {"page_number":1,"page_size":10,"d_from[payment_date]":"2018-08-01","d_to[payment_date]":"2018-08-03"}, class: CustomerPaymentQueryParamDto, errors: [{"target":{"page_number":1,"page_size":10,"d_from[payment_date]":"2018-08-01","d_to[payment_date]":"2018-08-03"},"value":"2018-08-01","property":"d_from[payment_date]","constraints":{"whitelistValidation":"property d_from[payment_date] should not exist"}},{"target":{"page_number":1,"page_size":10,"d_from[payment_date]":"2018-08-01","d_to[payment_date]":"2018-08-03"},"value":"2018-08-03","property":"d_to[payment_date]","constraints":{"whitelistValidation":"property d_to[payment_date] should not exist"}}]
期望结果
查询参数能被解析为以下结构:
{ d_from: { payment_date: '2018-08-01' }, d_to: { payment_date: '2018-08-03' }, page_number: 1, page_size: 10 }
解决方案
这个需求完全可以实现,问题出在查询参数的解析方式和类转换器的配置上。默认情况下,Express(NestJS默认HTTP适配器)不会自动将d_from[payment_date]这种格式的查询参数解析为嵌套对象,而是保留为扁平的键值对,导致类转换器无法匹配DTO中的d_from字段,触发白名单校验错误。
步骤1:启用Express的嵌套查询参数解析
在NestJS的主入口文件(main.ts)中,配置Express的query parser为extended: true,这样Express会将d_from[payment_date]解析为嵌套对象:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { urlencoded, json } from 'express'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 配置解析器,支持嵌套查询参数 app.use(urlencoded({ extended: true })); app.use(json()); await app.listen(3000); } bootstrap();
步骤2:调整DTO的类转换器配置(可选优化)
如果启用extended: true后仍有问题,可以给d_from和d_to字段添加自定义Transform装饰器,确保参数被正确转换:
import { Transform } from 'class-transformer'; // 在CustomerPaymentQueryParamDto中修改d_from和d_to字段 @IsOptional() @Type(() => DateFromTo) @ValidateNested() @Transform(({ value }) => typeof value === 'string' ? JSON.parse(value) : value) public readonly d_from?: DateFromTo; @IsOptional() @Type(() => DateFromTo) @ValidateNested() @Transform(({ value }) => typeof value === 'string' ? JSON.parse(value) : value) public readonly d_to?: DateFromTo;
步骤3:简化DateFromTo类
DateFromTo类不需要自定义构造函数,类转换器会自动实例化并赋值,可简化为:
import { IsOptional, IsString } from 'class-validator'; export class DateFromTo { @IsString() @IsOptional() payment_date?: string; @IsString() @IsOptional() create_date?: string; }
验证效果
配置完成后,发起原请求,查询参数会被正确解析为嵌套对象,通过Validation Pipe的校验,最终得到期望的结构。
内容的提问来源于stack exchange,提问作者Horrendo
相关产品推荐
相关产品推荐

