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

Node.js Fastify框架中Zod校验错误无法返回至请求响应的问题

Fastify中Zod校验错误无法返回响应的问题

我在使用Node.js的Fastify框架时遇到错误处理问题:Zod校验失败时,错误仅在终端输出,无法作为请求响应返回。在CreateUserController中执行Zod校验时,若请求数据不符合规则,错误只会出现在服务器终端,即使配置了错误处理中间件也无法捕获;但将相同的校验逻辑直接放在路由中时,却能正常返回错误。

相关代码示例

控制器代码

import { CreateUserService } from "./CreateUserService";
import z, { ZodError } from "zod";

import { FastifyReply, FastifyRequest } from "fastify";

class CreateUserController {
    constructor(private createUser: CreateUserService){}
    async handle(request: FastifyRequest, reply : FastifyReply){
       
        const schema = z.object({
            name: z.string(),
            address: z.string(),
            cellNumber: z.string().min(8)
        })
       
      
        const {name, address, cellNumber} =schema.parse(request.body)
      
       
       
        const user = await this.createUser.execute({name,address,cellNumber});
        return reply.send(user)
    }
}

export {CreateUserController};

路由代码

import { FastifyInstance } from "fastify";
import { createUserFactory } from "../modules/createUser/CreateUSerFactory";
import {z} from 'zod'

export  async function router (app:FastifyInstance){
    app.get("/", (request,reply)=>{
        return reply.send("OK")
    })

    app.post('/user',(request,reply)=>{
        const schema = z.object({
            name: z.string(),
            address: z.string(),
            cellNumber: z.string().min(8)
        })
        const {name, address, cellNumber} =schema.parse(request.body)
    
  //  createUserFactory().handle(request,reply);
})
}

问题原因

当在控制器的handle方法里调用schema.parse()时,Zod会直接抛出ZodError,但这个错误没有被Fastify的错误处理机制捕获——控制器的异步方法抛出的错误,需要被Fastify感知到才能转化为响应,而当前代码既没有手动捕获错误,也没有让Fastify处理这个未捕获的异常。

而直接在路由handler里使用schema.parse()时,Fastify会自动捕获路由函数中抛出的同步/异步错误,并将其转换为标准HTTP响应返回给客户端。

解决方案

方案1:控制器内手动捕获ZodError

用try/catch包裹校验逻辑,捕获到ZodError后通过reply主动返回错误响应:

import { CreateUserService } from "./CreateUserService";
import z, { ZodError } from "zod";
import { FastifyReply, FastifyRequest } from "fastify";

class CreateUserController {
    constructor(private createUser: CreateUserService){}
    async handle(request: FastifyRequest, reply : FastifyReply){
        const schema = z.object({
            name: z.string(),
            address: z.string(),
            cellNumber: z.string().min(8)
        });

        try {
            const {name, address, cellNumber} = schema.parse(request.body);
            const user = await this.createUser.execute({name,address,cellNumber});
            return reply.send(user);
        } catch (error) {
            if (error instanceof ZodError) {
                return reply.status(400).send({
                    statusCode: 400,
                    error: "Bad Request",
                    message: error.errors.map(e => `${e.path.join('.')}: ${e.message}`)
                });
            }
            // 处理其他未知错误
            return reply.status(500).send({
                statusCode: 500,
                error: "Internal Server Error"
            });
        }
    }
}

export {CreateUserController};

方案2:使用Zod的safeParse替代parse

safeParse不会直接抛出错误,而是返回包含校验结果的对象,可根据结果分支处理:

import { CreateUserService } from "./CreateUserService";
import z from "zod";
import { FastifyReply, FastifyRequest } from "fastify";

class CreateUserController {
    constructor(private createUser: CreateUserService){}
    async handle(request: FastifyRequest, reply : FastifyReply){
        const schema = z.object({
            name: z.string(),
            address: z.string(),
            cellNumber: z.string().min(8)
        });

        const result = schema.safeParse(request.body);
        if (!result.success) {
            return reply.status(400).send({
                statusCode: 400,
                error: "Bad Request",
                message: result.error.errors.map(e => `${e.path.join('.')}: ${e.message}`)
            });
        }

        const {name, address, cellNumber} = result.data;
        const user = await this.createUser.execute({name,address,cellNumber});
        return reply.send(user);
    }
}

export {CreateUserController};

方案3:利用Fastify原生请求校验(推荐)

Fastify原生支持请求参数校验,将Zod规则定义在路由的schema配置中,Fastify会自动完成校验并返回错误响应:

修改路由代码

import { FastifyInstance } from "fastify";
import { createUserFactory } from "../modules/createUser/CreateUSerFactory";
import {z} from 'zod'

export async function router (app:FastifyInstance){
    app.get("/", (request,reply)=>{
        return reply.send("OK")
    })

    app.post('/user', {
        schema: {
            body: z.object({
                name: z.string(),
                address: z.string(),
                cellNumber: z.string().min(8)
            })
        }
    }, async (request,reply)=>{
        // request.body已被Fastify自动校验,可直接使用
        await createUserFactory().handle(request,reply);
    })
}

修改控制器代码(移除内部校验逻辑)

import { CreateUserService } from "./CreateUserService";
import { FastifyReply, FastifyRequest } from "fastify";

class CreateUserController {
    constructor(private createUser: CreateUserService){}
    async handle(request: FastifyRequest, reply : FastifyReply){
        // 直接使用已校验的请求体
        const {name, address, cellNumber} = request.body as {
            name: string;
            address: string;
            cellNumber: string;
        };
        const user = await this.createUser.execute({name,address,cellNumber});
        return reply.send(user);
    }
}

export {CreateUserController};

内容的提问来源于stack exchange,提问作者Bruno Sampayo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 20:23:17