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

Sails.js请求全局上下文实现:如何统一管理请求上下文信息?

当然可以!这在Sails.js里是非常常见的需求,完全不用到处传递用户信息,有几种简洁的方案可以实现:

1. 扩展请求对象(最常用、最简单的方案)

Sails的请求对象(req)本身就是每个请求的上下文容器,你可以通过自定义中间件,把当前用户信息挂载到req上,这样在控制器、服务、甚至策略里都能直接访问。

实现步骤:

  • 在config/http.js中添加一个自定义中间件,用来从MongoDB获取用户信息并挂载到req:
// config/http.js
module.exports.http = {
  middleware: {
    // 调整中间件执行顺序,确保fetchCurrentUser在路由处理前执行
    order: [
      'cookieParser',
      'session',
      'fetchCurrentUser',
      'bodyParser',
      'compress',
      'poweredBy',
      'router',
      'www',
      'favicon',
    ],

    // 自定义中间件:获取当前用户
    fetchCurrentUser: async function(req, res, next) {
      // 这里假设你已经通过某种方式拿到currentUserId(比如JWT解析、session)
      const currentUserId = req.headers['x-user-id'] || req.session.userId;
      
      if (currentUserId) {
        try {
          // 从MongoDB查询用户文档
          req.currentUserDoc = await User.findOne({ id: currentUserId });
          req.currentUserId = currentUserId;
        } catch (err) {
          sails.log.error('获取当前用户失败:', err);
          // 根据业务需求处理错误,比如返回401未授权
          return res.unauthorized('无效用户');
        }
      }
      // 继续执行后续中间件/路由
      next();
    }
  }
};
  • 之后在控制器里就能直接用了:
// api/controllers/ProfileController.js
module.exports = {
  getProfile: async function(req, res) {
    // 直接从req获取用户信息
    const currentUser = req.currentUserDoc;
    return res.ok({
      userId: req.currentUserId,
      email: currentUser.email,
      username: currentUser.username
    });
  }
};

如果需要在服务中使用,只需要把req传递给服务方法即可,比如:

// api/services/UserService.js
module.exports = {
  updateUserSettings: async function(req, settings) {
    return await User.updateOne({ id: req.currentUserId }).set(settings);
  }
};

2. 请求上下文存储(无需传递req的场景)

如果你的业务逻辑在模型、定时任务或者不需要req的地方也需要访问当前用户,可以用Node.js的async_hooks模块实现一个请求级别的上下文存储,跟踪每个请求的用户信息。

实现示例:

  • 先创建一个上下文服务api/services/RequestContext.js:
// api/services/RequestContext.js
const async_hooks = require('async_hooks');
const contextStore = new Map();

// 创建async hook来跟踪请求上下文
const asyncHook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId) {
    // 继承父级上下文(确保异步操作中也能拿到)
    if (contextStore.has(triggerAsyncId)) {
      contextStore.set(asyncId, contextStore.get(triggerAsyncId));
    }
  },
  destroy(asyncId) {
    // 请求结束后清理上下文,避免内存泄漏
    contextStore.delete(asyncId);
  }
});

asyncHook.enable();

module.exports = {
  // 设置当前用户到上下文
  setCurrentUser(user) {
    const asyncId = async_hooks.executionAsyncId();
    contextStore.set(asyncId, { currentUser: user });
  },
  // 获取当前用户
  getCurrentUser() {
    const asyncId = async_hooks.executionAsyncId();
    return contextStore.get(asyncId)?.currentUser;
  },
  // 获取当前用户ID
  getCurrentUserId() {
    return this.getCurrentUser()?.id;
  }
};
  • 然后在之前的fetchCurrentUser中间件里添加一行:
// 在fetchCurrentUser中间件中获取到req.currentUserDoc后
RequestContext.setCurrentUser(req.currentUserDoc);
  • 现在在任何地方都能直接获取用户信息,比如模型的生命周期钩子:
// api/models/Post.js
module.exports = {
  attributes: { /* ... */ },
  beforeCreate: async function(values, proceed) {
    // 直接从上下文获取当前用户ID,无需传递req
    values.createdBy = RequestContext.getCurrentUserId();
    return proceed();
  }
};

注意事项

  • 扩展req的方案简单直观,是Sails中最推荐的方式,适合绝大多数场景;
  • 使用async_hooks的上下文存储要注意内存管理,确保请求结束后清理上下文;
  • 如果你的项目使用JWT验证,建议把JWT解析的逻辑也放在中间件里,和获取用户的逻辑结合起来。

内容的提问来源于stack exchange,提问作者Zhen Liu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 04:20:15