自定义的Zod验证中间件在对Express的查询参数进行验证时返回一个空对象

前端开发 2026-07-09

我正在做一个简单的副业项目来学习后端开发。我在已经搭建好的Express应用中使用了自定义的Zod验证中间件。它对 bodyparams 能正常工作,但在与 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 { }

解决方案

我找到了问题所在地。

在Express v5中,req.query不再允许直接重新赋值。这会导致问题:

req.query = parsedQuery;

相反,重新定义该属性:

Object.defineProperty(req, "query", { ...Object.getOwnPropertyDescriptor(req, "query"), writable: false, value: parsedQuery, });

相关讨论: 在Express v5中有没有办法修改 req.query

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章