You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 03:09:53