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

如何在Express.js应用级中间件中获取请求参数以校验MongoDB记录?

问题原因

Express 中,应用级中间件(app.use(checkRecordsExistence))会在路由匹配完成前执行,此时框架还未解析 URL 中的路由参数,所以req.params是空对象。而路由级中间件绑定在具体路由上,会在路由匹配、参数解析完成后执行,因此能正常获取req.params。

解决方案

方法1:在顶层路由统一挂载中间件(推荐)

创建一个根路由,先挂载校验中间件,再将所有子路由挂载到这个根路由下。这样所有请求经过根路由时,已经完成路由匹配和参数解析,中间件可以正常获取req.params,同时无需在每个子路由单独添加中间件,符合 DRY 原则。

示例代码:

const express = require('express');
const app = express();
const asyncHandler = require('express-async-handler');
const checkRecordsExistence = asyncHandler(async (req, res, next) => {
    const { params } = req;
    for (const key in params) {
        if (Object.hasOwnProperty.call(params, key)) {
            if (key.endsWith('Id')) {
                const modelNameLower = key.slice(0, -2);
                const modelName = modelNameLower.charAt(0).toUpperCase() + modelNameLower.slice(1);

                const Model = mongoose.model(modelName);
                const recordId = params[key];
                const record = await Model.findById(recordId);
                if (!record) {
                    return next(new ErrorResponse(
                        new Error(message.notFound(modelName)),
                        404
                    ));
                }
                req[`_${modelNameLower}`] = record;
            }
        }
    }
    next();
});

// 创建根路由实例
const apiRouter = express.Router();

// 根路由统一挂载校验中间件
apiRouter.use(checkRecordsExistence);

// 挂载各业务子路由到根路由
apiRouter.use('/users', require('./routes/users'));
apiRouter.use('/posts', require('./routes/posts'));
// ...其他业务路由

// 将根路由挂载到应用
app.use('/api', apiRouter);

方法2:优化中间件挂载路径(适配简单路由场景)

如果你的路由直接定义在app上,可通过通配符路径挂载中间件,确保它在路由匹配后执行。比如:

// 匹配所有包含以Id结尾参数的路径
app.use(/\/.*[a-zA-Z]+Id\/?/, checkRecordsExistence);

// 定义业务路由
app.get('/users/:userId', (req, res) => { ... });
app.get('/posts/:postId', (req, res) => { ... });

不过这种方式灵活性较差,仅适合路由结构简单的场景。

额外优化建议
  • 缓存模型实例:避免每次调用mongoose.model(modelName),可提前导入所有模型并存储在对象中,提升性能:
    // 提前导入模型
    const models = {
        User: require('./models/User'),
        Post: require('./models/Post')
        // ...其他模型
    };
    // 中间件中替换为
    const Model = models[modelName];
    
  • 添加跳过校验的开关:若某些路由无需校验,可在路由处理函数中设置req.skipRecordCheck = true,并在中间件开头判断:
    if (req.skipRecordCheck) return next();
    

内容的提问来源于stack exchange,提问作者Trung Nguyễn Hoàn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 14:13:25