You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

@fastify/multipart 与 fastify-type-provider-zod 集成时Schema验证失效及Swagger适配问题咨询

@fastify/multipart 与 fastify-type-provider-zod 集成时Schema验证失效及Swagger适配问题咨询

我之前也踩过这个坑!核心原因就是你说的——当设置attachFieldsToBody: "keyValues"时,@fastify/multipart会把文件字段直接转成Buffer,而你的Zod Schema里用的z.file()是期望拿到multipart原生的File对象,类型不匹配直接导致验证失效。同时还要兼顾Swagger的正确渲染,下面是我亲测可行的解决方案:

1. 修正multipart配置,保留File实例并适配Swagger

首先把attachFieldsToBody"keyValues"改成true,这样文件会以File实例的形式保留,同时加上sharedSchemaId让Swagger能识别文件上传控件:

import formbody from "@fastify/formbody";
import multipart from "@fastify/multipart";
import fp from "fastify-plugin";

export default fp(async (instance) => {
  await Promise.all([
    instance.register(formbody),
    instance.register(multipart, {
      attachFieldsToBody: true, // 保留File实例,替换原"keyValues"
      sharedSchemaId: '#MultipartFile', // 关键:让Swagger识别文件类型
    }),
  ]);
});

2. 用zMultipart()重构Zod Schema

zMultipart()是@fastify/multipart专为Zod提供的扩展,它会自动适配attachFieldsToBody: true的字段结构——把普通字段的value提取成原始值,文件字段保留File实例,完美匹配z.file()的验证要求:

import { z } from 'zod';
import { zMultipart } from '@fastify/multipart';

export const CreateCategoryRequestSchema = zMultipart({
  name: z.string(),
  path: z.string(),
  predefinedPath: z.string().optional(),
  file: z.file()
    .max(CategoryImageMaxSize)
    .mime(["image/jpeg", "image/png", "image/webp"]),
});

3. 路由配置确保类型推断生效

记得用withTypeProvider<ZodTypeProvider>包裹路由,保证类型推断和验证逻辑联动:

import type { ZodTypeProvider } from 'fastify-type-provider-zod';

fastify.withTypeProvider<ZodTypeProvider>().post(
  "/admin/categories",
  {
    schema: {
      body: CreateCategoryRequestSchema,
      consumes: ["multipart/form-data"],
      response: {
        201: SuccessResponseSchema(CreateCategoryResponseSchema),
      },
      tags: ["Admin"],
    },
  },
  async (req, reply) => {
    req.log.info({ body: req.body }, "BODY");
    // 此时req.body.name直接是字符串,req.body.file是File实例,无需额外处理
    reply.status(201).send({
      status: "success",
      data: {
        id: 1,
        name: "",
        path: "",
        imageId: null,
        imageUrl: null,
      },
    });
  },
);

额外注意点

  • 别再用attachFieldsToBody: "keyValues"了,它会强制把文件转成Buffer,完全破坏z.file()的验证逻辑;
  • sharedSchemaId: '#MultipartFile'不能省略,否则Swagger会把file字段当成普通文本框,无法显示文件上传按钮;
  • 如果不想用zMultipart(),也可以手动给每个字段加transform提取值,但这种方式繁琐且容易出错,不如官方提供的扩展可靠。

这样调整后,Schema验证会正常触发,Swagger也能正确渲染出multipart/form-data的上传表单,完美解决你的问题!

火山引擎 最新活动