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
相关产品推荐
相关产品推荐

