自定义Zod验证中间件验证Express查询参数时返回空对象
Express中Zod验证中间件处理Query参数异常问题
我正在开发一个后端学习项目,基于Express框架搭建,自定义了Zod验证中间件。该中间件处理body和params参数时完全正常,但处理query参数时,请求要么挂起无响应,要么返回空对象。
相关代码
验证中间件代码
import type { NextFunction, Request, Response } from "express"; import type { ZodObject, ZodType } from "zod"; import { ZodError } from "zod"; import { HttpError } from "../errors/http-errors"; type Schema = ZodObject | ZodType; type ParamsRecord = Record<string, string>; type QueryRecord = Record<string, unknown>; export interface RequestValidationSchema { body?: Schema; params?: Schema; query?: Schema; } const formatedError = (error: ZodError) => error.issues.map((issue) => ({ path: issue.path.join("."), message: issue.message, })); export const validateRequest = (schema: RequestValidationSchema) => { return (req: Request, _res: Response, next: NextFunction) => { try { if (schema.body) { const parsedBody = schema.body.parse(req.body) as unknown; req.body = parsedBody; } if (schema.params) { const parsedParams = schema.params.parse( req.params, ) as ParamsRecord; req.params = parsedParams as Request["params"]; } if (schema.query) { const parsedQuery = schema.query.parse( req.query, ) as QueryRecord; req.query = parsedQuery as Request["query"]; } next(); } catch (error) { if (error instanceof ZodError) { next( new HttpError(400, "Validation Error", { issue: formatedError(error), }), ); } else { next(error); } } }; };
路由配置
router.patch( "/promotions/:id/toggle-active", authenticate, canAccess(["admin", "manager"]), validateRequest({params: PromotionParamSchema, query:TogglePromotionActiveSchema}), togglePromotionActiveHandler );
测试请求代码
describe("PATCH /promotions/:id/toggle-active - Toggle Promotion Active Status", () => { it("should deactivate promotion", async () => { const promotion = await createPromotionHelper(); const response = await request(app) .patch(`/sale/promotions/${promotion.data.id}/toggle-active?isActive=false`) .set("Cookie", [`accessToken=${adminAccessToken}`]) .send(); console.log('res',response.body) console.log('res',response.body.message.details) expect(response.statusCode).toBe(200); expect(response.body.success).toBe(true); const verifyResponse = await request(app) .get(`/sale/promotions/${promotion.data.id}`) .send(); console.log('ver',verifyResponse.body) expect(verifyResponse.body.data.isActive).toBe(false); });
控制台输出
res { }
问题原因与修复方案
1. Query参数类型不兼容
Express的req.query类型是ParsedQs(由qs库解析生成),而非普通的Record<string, unknown>。直接将Zod解析后的普通对象赋值给req.query会导致类型冲突,甚至运行时异常。
修复方式:
改用Object.assign合并解析后的参数,避免直接替换:
if (schema.query) { const parsedQuery = schema.query.parse(req.query) as QueryRecord; Object.assign(req.query, parsedQuery); }
或者调整类型定义,确保兼容ParsedQs:
import type { ParsedQs } from "qs"; type QueryRecord = ParsedQs & Record<string, unknown>; // 赋值时 req.query = parsedQuery as ParsedQs;
2. Query参数类型未转换
URL中的Query参数默认都是字符串类型(比如isActive=false实际传递的是字符串"false"),如果Zod Schema定义的是z.boolean(),默认会验证失败,因为字符串不等于布尔值。
修复方式:
使用Zod的coerce方法自动转换类型:
import { z } from "zod"; // 正确的Schema定义 const TogglePromotionActiveSchema = z.object({ isActive: z.coerce.boolean() });
3. 全局错误处理缺失
如果验证失败抛出HttpError后,没有对应的全局错误处理中间件返回响应,Express会无法结束请求,导致请求挂起并返回空对象。
修复方式:
添加全局错误处理中间件:
app.use((err: unknown, req: Request, res: Response, next: NextFunction) => { if (err instanceof HttpError) { return res.status(err.statusCode).json({ success: false, message: err.message, details: err.details }); } // 处理其他未知错误 res.status(500).json({ success: false, message: "Internal Server Error" }); });
4. 调试验证错误
可以在中间件的catch块添加日志,定位具体的验证失败字段:
catch (error) { if (error instanceof ZodError) { console.log("Zod验证错误详情:", error.issues); next( new HttpError(400, "Validation Error", { issue: formatedError(error), }), ); } else { console.log("意外错误:", error); next(error); } }
内容的提问来源于stack exchange,提问作者BIPIN
相关产品推荐
相关产品推荐

