已安装express-async-errors,为何仍无法捕获Zod错误?
全局错误处理器无法捕获Zod验证错误(Express + Zod)
我正在开发一款API,尝试通过Zod配置全局错误控制器,但始终无法生效。即使已安装并使用express-async-errors,全局错误处理器handleErrors似乎并未被调用。
错误管理文件
import { NextFunction, Request, Response } from "express"; import { ZodError } from "zod"; class AppError extends Error { statusCode: number; constructor(message: string, statusCode: number = 400) { super(message); this.statusCode = statusCode; } } const handleErrors = (err: Error, req: Request, res: Response, next: NextFunction) => { console.log('The error handler have been called'); if(err instanceof AppError) { return res.status(err.statusCode).json({message: err.message}) } if (err instanceof ZodError) { return res.status(400).json({ message: err.flatten().fieldErrors, okok:'okok' }); } return res.status(500).json({message: 'Internal server error'}) } export { AppError, handleErrors };
Zod Schema定义
import { z } from "zod" const moviesSchema = z.object({ id: z.number(), name: z.string().max(50), description: z.string().optional().nullish(), duration: z.number(), price: z.number() }) const moviesRequestSchema = moviesSchema.omit({id: true}) export {moviesRequestSchema, moviesSchema}
验证中间件
import { NextFunction, Request, Response } from "express" import { ZodTypeAny } from "zod" const checkIfCreateDataIsValid = (schema: ZodTypeAny) => (req: Request, res: Response, next: NextFunction) => { const validatedDate = schema.parse(req.body) req.body = validatedDate return next() } export default checkIfCreateDataIsValid
路由配置
import { Router } from "express"; import { createMoviesController } from "../controllers/movies.controller"; import checkIfCreateDataIsValid from "../middlewares/checkIfCreateDataIsValid.middleware"; import { moviesRequestSchema } from "../schemas/movies.schemas"; const moviesRoutes: Router = Router() moviesRoutes.post('', checkIfCreateDataIsValid(moviesRequestSchema), createMoviesController) export default moviesRoutes
app.ts配置
import "reflect-metadata" import "express-async-errors" import express, { Application } from "express"; import { handleErrors } from "./errors"; import moviesRoutes from "./routes/movies.routes"; const app: Application = express() app.use(handleErrors) app.use(express.json()) app.use('/movies', moviesRoutes) export default app
错误现象
当我发送带有错误参数(如price传入字符串)的POST请求时,控制台并未打印"The error handler have been called",而是直接抛出原始ZodError:
ZodError: [ { "code": "invalid_type", "expected": "number", "received": "string", "path": [ "price" ], "message": "Expected number, received string" } ] at Object.get error [as error] (C:\Users\arauj\Downloads\projetos\m4-movies-typeorm-H3isenberg8\node_modules\zod\lib\types.js:43:31) at ZodObject.parse (C:\Users\arauj\Downloads\projetos\m4-movies-typeorm-H3isenberg8\node_modules\zod\lib\types.js:141:22) at C:\Users\arauj\Downloads\projetos\m4-movies-typeorm-H3isenberg8\src\middlewares\checkIfCreateDataIsValid.middleware.ts:5:34 at newFn (C:\Users\arauj\Downloads\projetos\m4-movies-typeorm-H3isenberg8\node_modules\express-async-errors\index.js:16:20) at Layer.handle [as handle_request] (C:\Users\arauj\Downloads\projetos\m4-movies-typeorm-H3isenberg8\node_modules\express\lib\router\layer.js:95:5) at next (C:\Users\arauj\Downloads\projetos\m4-movies-typeorm-H3isenberg8\node_modules\express\lib\router\route.js:144:13) at Route.dispatch (C:\Users\arauj\Downloads\projetos\m4-movies-typeorm-H3isenberg8\node_modules\express\lib\router\route.js:114:3) at newFn (C:\Users\arauj\Downloads\projetos\m4-movies-typeorm-H3isenberg8\node_modules\express-async-errors\index.js:16:20) at Layer.handle [as handle_request] (C:\Users\arauj\Downloads\projetos\m4-movies-typeorm-H3isenberg8\node_modules\express\lib\router\layer.js:95:5) at C:\Users\arauj\Downloads\projetos\m4-movies-typeorm-H3isenberg8\node_modules\express\lib\router\index.js:284:15
解决方案
1. 调整全局错误中间件的顺序
Express中间件按注册顺序执行,你当前把handleErrors放在最前面,此时请求还未经过路由和其他中间件,错误无法被捕获。必须将全局错误中间件放在所有路由和其他中间件之后:
修改app.ts:
import "reflect-metadata" import "express-async-errors" import express, { Application } from "express"; import { handleErrors } from "./errors"; import moviesRoutes from "./routes/movies.routes"; const app: Application = express() // 先注册JSON解析中间件 app.use(express.json()) // 再注册业务路由 app.use('/movies', moviesRoutes) // 最后注册全局错误中间件 app.use(handleErrors) export default app
2. (可选)用Zod的safeParse手动处理错误
Zod的parse是同步抛出错误,Express本身会捕获同步错误并传递给错误中间件,但顺序正确是前提。如果想更稳妥,可改用safeParse手动传递错误:
修改验证中间件:
import { NextFunction, Request, Response } from "express" import { ZodError, ZodTypeAny } from "zod" const checkIfCreateDataIsValid = (schema: ZodTypeAny) => (req: Request, res: Response, next: NextFunction) => { const result = schema.safeParse(req.body) if (!result.success) { // 将ZodError传递给错误中间件 return next(result.error) } req.body = result.data return next() } export default checkIfCreateDataIsValid
3. 修正拼写错误
错误处理器中返回的Intesdarnal server error是拼写错误,建议改为Internal server error,不影响功能但提升规范性。
内容的提问来源于stack exchange,提问作者NewDev
相关产品推荐
相关产品推荐

