NestJS中Multipart Form Data下嵌套DTO验证失败问题
NestJS 嵌套DTO在Multipart Form Data下验证失败的解决方案
问题原因
Multipart Form Data 请求默认以键值对形式传递数据,不会自动将嵌套的JSON字符串转换为对象,而class-validator和class-transformer需要接收对象类型才能完成嵌套验证,因此出现nested property body must be either object or array的错误。
解决方案
方案一:修改DTO,手动解析JSON字符串
调整CreateHighlightDto,将body字段先按字符串接收,再通过class-transformer的Transform装饰器解析为JSON对象,同时保留嵌套验证规则:
import { Type, Transform } from 'class-transformer'; import { IsArray, IsNotEmpty, IsString, MaxLength, ArrayMaxSize, ValidateNested, } from 'class-validator'; class CreateHighlightBody { @IsString() @MaxLength(255) title: string; @IsArray() @ArrayMaxSize(10) content: string[]; @IsNotEmpty() @IsString() game: string; } export class CreateHighlightDto { @IsString() // 先验证是字符串格式 @Transform(({ value }) => { // 解析JSON字符串为对象,添加错误处理避免解析失败 try { return JSON.parse(value); } catch (e) { throw new Error('body必须是合法的JSON字符串'); } }) @ValidateNested() @Type(() => CreateHighlightBody) body: CreateHighlightBody; }
测试传值方式
在Insomnia的Multipart Form Data表单中,body字段的值需要传入JSON格式的字符串,例如:
{"title":"测试高光","content":["击杀片段","推塔片段"],"game":"王者荣耀"}
方案二:自定义管道处理Form Data嵌套对象
如果不想修改DTO结构,可以创建自定义管道,自动解析Form Data中的嵌套字段:
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common'; @Injectable() export class ParseNestedFormDataPipe implements PipeTransform { transform(value: any) { try { // 遍历字段,尝试将字符串解析为JSON对象 Object.keys(value).forEach(key => { if (typeof value[key] === 'string') { try { value[key] = JSON.parse(value[key]); } catch {} } }); return value; } catch (e) { throw new BadRequestException('嵌套表单数据格式无效'); } } }
控制器中使用管道
@Post('create') @Auth() @UseInterceptors(FileInterceptor('file')) async create( @CurrentUser() user: UserCurrent, @Body(new ParseNestedFormDataPipe()) dto: CreateHighlightDto, @UploadedFile(new ParseFilePipe()) file: Express.Multer.File ) { return this.highlightService.createHighlight(user, file, dto.body); }
使用这种方式后,在Insomnia中可以直接将body字段的类型选为JSON,传入对象结构即可(部分API工具支持直接选择JSON类型传值)。
注意事项
- 方案一中要确保传递的
body是合法JSON字符串,避免解析失败; - 方案二的管道会尝试解析所有字符串字段,若有不需要转换的字段,可添加白名单或过滤逻辑。
内容的提问来源于stack exchange,提问作者miliereya
相关产品推荐
相关产品推荐

