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

如何为NestJS v10构建兼容文件上传的自定义Zod验证管道

NestJS v10 自定义Zod验证器兼容文件上传解决方案

问题诊断

当前实现中attachment返回undefined的核心原因:

  • 全局绑定的ZodValidationPipe会处理所有请求参数,当参数类型不是ZodDtoClass时,代码执行if (!schemaClass.schema) return;直接返回undefined,覆盖了@UploadedFile获取到的文件对象。
  • 验证器返回{ data: result.data }的嵌套结构,导致DTO实例不符合常规使用习惯。

解决方案

1. 修复ZodValidationPipe逻辑

修改验证器,仅处理ZodDtoClass类型的参数,其他参数直接返回原始值:

// zod-validation.pipe.ts
import { Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { ZodDtoClass } from '../utils/zod-dto.interface';

@Injectable()
export class ZodValidationPipe {
  transform(value: unknown, metadata: ArgumentMetadata) {
    // 仅处理带有schema静态属性的DTO类
    if (!metadata.metatype || !('schema' in metadata.metatype)) {
      return value;
    }

    const schemaClass = metadata.metatype as typeof ZodDtoClass;
    if (!schemaClass.schema) {
      return value;
    }

    const result = schemaClass.schema.safeParse(value);
    if (!result.success) {
      throw new BadRequestException(result.error.flatten());
    }

    // 直接返回验证后的数据,取消嵌套的data结构
    return result.data;
  }
}

2. 完善ZodDtoClass抽象类

定义标准的抽象类约束DTO结构,支持类型推断:

// zod-dto.interface.ts
import { z, ZodSchema } from 'zod';

export abstract class ZodDtoClass<T extends ZodSchema> {
  static schema: ZodSchema;

  constructor(data: z.infer<T>) {
    Object.assign(this, data);
  }
}

3. 调整DTO实现

补充属性声明以获得完整的类型提示:

// create-feed.dto.ts
import { z } from 'zod';
import { ZodDtoClass } from '../../utils/zod-dto.interface';

const schema = z
  .object({
    title: z.string().min(5).max(35).nullish(),
    body: z.string().max(140).nullish(),
  })
  .refine(
    (d) => {
      return !!(d.title || d.body);
    },
    {
      path: ['title'],
      message: "标题、正文和附件不能同时为空",
    },
  );

export class CreateFeedDto extends ZodDtoClass<typeof schema> {
  static override schema = schema;

  // 显式声明属性,提升IDE类型提示
  title?: string;
  body?: string;
}

4. 验证控制器效果

调整后控制器可以正常获取文件和验证后的DTO:

@UseInterceptors(FileInterceptor('attachment'))
@Post('/create')
create(
  @Body() createFeedDto: CreateFeedDto,
  @UploadedFile(
    new ParseFilePipe({
      validators: [
        new MaxFileSizeValidator({ maxSize: 5000000 }),
        new FileTypeValidator({ fileType: '(jpg|png)$' }),
      ],
    }),
  )
  attachment: Express.Multer.File,
) {
  console.log({
    createFeedDto, // 输出:{ title: 'hello', body: 'This is body text' }
    attachment, // 正常输出文件对象
  });

  return this.feedsService.create(createFeedDto);
}

额外优化:Zod直接验证文件字段

如果需要在Zod Schema中统一验证文件,可以将文件字段加入Schema,结合AnyFilesInterceptor将文件合并到请求体(或自定义逻辑):

// create-feed.dto.ts
const schema = z
  .object({
    title: z.string().min(5).max(35).nullish(),
    body: z.string().max(140).nullish(),
    attachment: z.object({
      originalname: z.string(),
      size: z.number().max(5000000),
      mimetype: z.string().regex(/(jpg|png)$/),
    }).nullish(),
  })
  .refine(
    (d) => {
      return !!(d.title || d.body || d.attachment);
    },
    {
      path: ['title'],
      message: "标题、正文和附件不能同时为空",
    },
  );

内容的提问来源于stack exchange,提问作者Md. A. A. Apu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:35:57