如何在Swagger与Controller中使用不同的请求体Schema?
问题描述
我有一个创建新用户的Controller,请求体CreateUser包含可选字段locationName。我通过CreateUserLocationPipe管道将请求体中未定义的locationName转为null。运行时locationName绝不会是undefined,Swagger中也显示该字段为可选,但在代码中访问createUserInput.locationName时,TypeScript仍提示该值可能为undefined。请问如何为Controller代码和Swagger分别设置不同的类型?
User控制器代码:
@Post("/users") async createUser( @Body(CreateUserLocationPipe) createUserInput: CreateUser ): Promise<User> { return await this.usersService.create(createUserInput); }
CreateUser类:
export class CreateUser { @ApiProperty({ description: "用户邮箱" }) email: string; @ApiProperty({ description: "用户所在地" }) locationName: string | undefined | null; }
CreateUserLocationPipe管道:
@Injectable() export class CreateUserLocationPipe { transform(value: string | null | undefined): string | null { if (value.locationName === undefined) { value.locationName = null; } } }
解决方案
核心思路是拆分Swagger请求类型和Controller处理后类型,让前者保留undefined以支持Swagger的可选字段展示,后者移除undefined让TypeScript正确识别运行时类型。
步骤1:拆分类型定义
保留CreateUser作为Swagger的请求输入类型,同时定义一个处理后的类型,确保locationName仅为string | null:
// 用于Swagger的请求输入类型,显式标记字段可选 export class CreateUser { @ApiProperty({ description: "用户邮箱" }) email: string; @ApiProperty({ description: "用户所在地", required: false }) locationName?: string | null; } // 处理后的类型,移除undefined,确保运行时类型准确 export type ProcessedCreateUser = Omit<CreateUser, 'locationName'> & { locationName: string | null; };
步骤2:修复管道的类型与逻辑
修正管道的输入输出类型,确保它能正确转换CreateUser为ProcessedCreateUser,同时补全返回逻辑:
@Injectable() export class CreateUserLocationPipe implements PipeTransform<CreateUser, ProcessedCreateUser> { transform(value: CreateUser): ProcessedCreateUser { if (value.locationName === undefined) { value.locationName = null; } return value as ProcessedCreateUser; } }
步骤3:更新Controller的类型声明
在Controller中指定处理后的类型,让TypeScript识别createUserInput的实际运行时类型:
@Post("/users") async createUser( @Body(CreateUserLocationPipe) createUserInput: ProcessedCreateUser ): Promise<User> { // 此时访问createUserInput.locationName,TypeScript不会再提示可能为undefined return await this.usersService.create(createUserInput); }
补充说明
- Swagger会基于
CreateUser类生成文档,@ApiProperty({ required: false })确保字段显示为可选; - 管道处理后,
createUserInput的类型是ProcessedCreateUser,TypeScript能准确判断locationName只能是string或null; - 原管道代码的参数类型错误,已修正为接收
CreateUser对象,而非string | null | undefined。
内容的提问来源于stack exchange,提问作者Eranga Heshan
相关产品推荐
相关产品推荐

