如何在Express JS中从一个中间件调用另一个中间件?
中间件调用修正、优化方案及设计模式建议
核心问题:中间件调用方式错误
你直接调用jwt.auth()是无效的——因为jwt.auth()返回的是一个(req, res, next) => {}格式的中间件函数,必须传入当前请求的req、res对象,以及回调函数来处理中间件的完成或错误逻辑。
正确的中间件调用方式
封装一个通用工具函数,把Express中间件转换成Promise,方便在async函数中按顺序执行:
// 通用工具:执行单个中间件并返回Promise const runMiddleware = (req, res, middleware) => { return new Promise((resolve, reject) => { middleware(req, res, (err) => { if (err) reject(err); else resolve(); }); }); }; // 修正后的validatePolicies const validatePolicies = (category) => async (req, res, next) => { try { // 按顺序执行所有验证逻辑 await runMiddleware(req, res, jwt.auth()); req.user = await users.sessionUserInfo(req, res); // 若sessionUserInfo是中间件,同样用runMiddleware调用 await runMiddleware(req, res, common.checkBasicValidations); await runMiddleware(req, res, rbac.checkRBACAccess()); await runMiddleware(req, res, resource.checkResourceAccess(category)); await runMiddleware(req, res, auth.checkAuthorized()); } catch (err) { log.warn("Problem in validatePolicies: ", err); return next(ApiError.internal("Something went wrong")); } next(); };
优化方案:减少路由重复代码
方案1:预定义通用中间件栈
把每个路由重复的中间件提前组合成数组,路由中直接扩展使用,既保留中间件的独立逻辑,又简化路由代码:
// 定义通用中间件集合 const commonAuthStack = [ jwt.auth(), common.checkBasicValidations, auth.checkAuthorized() ]; // 路由中直接复用 router.post("/v1/user", ...commonAuthStack, auth.checkRBACAccess([auth.access.user], [auth.operation.create, auth.operation.update], auth.checkDomainAccessFromUserEmail, ["email"]), auth.checkRBACAccess([auth.access.user], [auth.operation.create, auth.operation.update], auth.checkDomainAccessFromUserId, ["_id"]), users.postUser ); router.get("/v1/user", ...commonAuthStack, policy.validatePolicies("course"), // 只保留差异化逻辑 users.getLoginguserInfo );
方案2:修复错误处理逻辑
原代码中存在next重复调用、变量未定义的问题,修正后只调用一次next传递错误:
// 错误写法 // return next(ApiError.internal("Something went wrong")) && next(e.message); // 正确写法 return next(ApiError.internal("Something went wrong", err.message));
设计模式建议
策略模式:动态匹配验证逻辑
针对不同业务分类(如"course"、"user")定义不同的验证策略,新增分类时只需扩展策略配置,无需修改核心验证逻辑:
// 定义各类业务的验证策略栈 const policyStrategies = { course: [ jwt.auth(), common.checkBasicValidations, rbac.checkRBACAccess([auth.access.course], [auth.operation.read]), resource.checkResourceAccess("course"), auth.checkAuthorized() ], user: [ jwt.auth(), common.checkBasicValidations, rbac.checkRBACAccess([auth.access.user], [auth.operation.read, auth.operation.update]), resource.checkResourceAccess("user"), auth.checkAuthorized() ] }; const validatePolicies = (category) => async (req, res, next) => { const strategy = policyStrategies[category]; if (!strategy) { return next(ApiError.badRequest("Invalid policy category")); } try { // 依次执行策略中的所有中间件 for (const middleware of strategy) { await runMiddleware(req, res, middleware); } } catch (err) { log.warn("Policy validation failed: ", err); return next(ApiError.internal("Policy validation failed")); } next(); };
责任链模式:复用Express中间件机制
Express本身基于责任链模式设计,每个中间件作为链上节点依次执行,失败则终止流程。通过组合中间件栈的方式,天然符合责任链设计思路,既保证逻辑独立性,又能灵活调整执行顺序。
内容的提问来源于stack exchange,提问作者justAnAnotherCoder
相关产品推荐
相关产品推荐

