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

NestJS嵌套DTO类验证失败及Swagger Schema异常求助

问题分析与解决方案

问题根源

  1. Swagger Schema显示异常:父DTO中的数组字段originDestinations和travelers仅用@ApiProperty()装饰,未明确指定数组内的嵌套类型,导致Swagger默认解析为字符串数组。
  2. Postman验证错误:请求体未按嵌套结构传递数据,或class-transformer未正确将JSON转换为DTO实例,触发class-validator的字段校验规则。

修正后的DTO代码

import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsNotEmpty, IsArray, ValidateNested, IsDateString, MinLength, MaxLength, IsEnum } from "class-validator";

// 定义枚举类型,增强类型安全性
export enum TravelerType {
  ADULT = "ADULT",
  CHILD = "CHILD",
  SENIOR = "SENIOR",
  YOUNG = "YOUNG",
  HELD_INFANT = "HELD_INFANT",
  SEATED_INFANT = "SEATED_INFANT",
  STUDENT = "STUDENT"
}

export enum FareOption {
  STANDARD = "STANDARD",
  INCLUSIVE_TOUR = "INCLUSIVE_TOUR",
  SPANISH_MELILLA_RESIDENT = "SPANISH_MELILLA_RESIDENT",
  SPANISH_CEUTA_RESIDENT = "SPANISH_CEUTA_RESIDENT",
  SPANISH_CANARY_RESIDENT = "SPANISH_CANARY_RESIDENT",
  SPANISH_BALEARIC_RESIDENT = "SPANISH_BALEARIC_RESIDENT",
  AIR_FRANCE_METROPOLITAN_DISCOUNT_PASS = "AIR_FRANCE_METROPOLITAN_DISCOUNT_PASS",
  AIR_FRANCE_DOM_DISCOUNT_PASS = "AIR_FRANCE_DOM_DISCOUNT_PASS",
  AIR_FRANCE_COMBINED_DISCOUNT_PASS = "AIR_FRANCE_COMBINED_DISCOUNT_PASS",
  AIR_FRANCE_FAMILY = "AIR_FRANCE_FAMILY",
  ADULT_WITH_COMPANION = "ADULT_WITH_COMPANION",
  COMPANION = "COMPANION"
}

class Travelers {
  @ApiProperty({ enum: TravelerType })
  @IsEnum(TravelerType)
  travelerType: TravelerType;

  @ApiProperty({ enum: FareOption, isArray: true })
  @IsEnum(FareOption, { each: true })
  fareOptions: FareOption[];
}

class OriginDestinations {
  @ApiProperty()
  @IsNotEmpty()
  @MinLength(3)
  @MaxLength(3)
  originLocationCode: string;

  @ApiProperty()
  @IsNotEmpty()
  @MinLength(3)
  @MaxLength(3)
  destinationLocationCode: string;

  @ApiProperty()
  @IsDateString()
  @IsNotEmpty()
  departureDate: string;
}

export class OriginDestinationsDto { // 类名采用大驼峰命名,符合TS规范
  @IsArray()
  @ApiProperty({ type: () => OriginDestinations, isArray: true }) // 指定嵌套类型和数组标识
  @IsNotEmpty()
  @ValidateNested({ each: true })
  @Type(() => OriginDestinations)
  originDestinations: OriginDestinations[];

  @ApiProperty({ type: () => Travelers, isArray: true }) // 同上
  @IsArray()
  @IsNotEmpty()
  @ValidateNested({ each: true })
  @Type(() => Travelers)
  travelers: Travelers[];
}

关键修改说明

  1. Swagger Schema修复:在父DTO的数组字段上,为@ApiProperty添加type: () => 嵌套类和isArray: true参数,明确告知Swagger数组内的对象结构。
  2. 增强类型安全:将枚举值定义为TS枚举类型,配合@IsEnum校验,避免字符串拼写错误。
  3. 规范类命名:将originDestinationsDto改为OriginDestinationsDto,遵循TypeScript大驼峰命名规范。
  4. 完善枚举校验:为fareOptions添加@IsEnum({ each: true }),确保数组中每个元素都符合枚举规则。

额外配置检查

确保在NestJS的main.ts中启用全局验证管道并开启自动转换,否则class-transformer无法将JSON请求体转换为DTO实例:

import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({
    transform: true, // 自动转换请求体为DTO实例
    whitelist: true, // 过滤DTO中未定义的字段
  }));
  await app.listen(3000);
}
bootstrap();

Postman请求示例

使用正确的嵌套结构发送请求:

{
  "originDestinations": [
    {
      "originLocationCode": "PEK",
      "destinationLocationCode": "SHA",
      "departureDate": "2024-10-01"
    }
  ],
  "travelers": [
    {
      "travelerType": "ADULT",
      "fareOptions": ["STANDARD"]
    }
  ]
}

内容的提问来源于stack exchange,提问作者jatin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 09:05:20