@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的上传表单,完美解决你的问题!




