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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 22:30:49