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

如何将Express的req.user传入Sequelize全局钩子自动填充用户字段

Sequelize全局自动注入createdBy/updatedBy实现方案

核心思路是利用Node.js原生的异步上下文存储能力,在请求链路中全局保存当前用户信息,钩子函数直接从上下文读取用户ID,不需要手动传参,也不需要逐个模型注册钩子。


步骤1:初始化全局异步上下文

用Node.js内置的AsyncLocalStorage实现跨异步调用的上下文共享,不需要额外安装第三方依赖,稳定性远高于早期的第三方CLS库。

// src/common/request-context.js
const { AsyncLocalStorage } = require('async_hooks');
const localStorage = new AsyncLocalStorage();

module.exports = {
  /**
   * 在请求入口绑定当前用户到上下文
   * @param {object} user 当前登录用户
   * @param {Function} next 后续请求处理逻辑
   */
  runWithUser: (user, next) => localStorage.run({ user }, next),
  /**
   * 供全局钩子调用,获取当前请求上下文的用户
   * @returns {object|null} 当前登录用户
   */
  getCurrentUser: () => localStorage.getStore()?.user || null
}

步骤2:Express中间件绑定上下文

在鉴权中间件之后、业务路由之前加一层全局中间件,把解析好的req.user绑定到异步上下文里,整个请求链路内的所有异步操作都能直接读取到这个用户,不需要层层传参。

const { runWithUser } = require('./common/request-context');

// 注意:这个中间件要放在鉴权中间件之后,确保req.user已经被解析完成
app.use((req, res, next) => {
  runWithUser(req.user || null, next);
});

步骤3:注册全局Sequelize钩子

直接在sequelize实例上注册全局钩子,不需要逐个模型配置,钩子函数直接从上下文读取用户ID自动赋值,和内置timestamps的自动注入逻辑完全一致。

const { getCurrentUser } = require('./common/request-context');

// 单条创建自动填充createdBy、updatedBy
function autoSetCreateUser(instance) {
  const user = getCurrentUser();
  if (user?.id) {
    instance.createdBy = user.id;
    instance.updatedBy = user.id;
  }
}

// 更新操作自动填充updatedBy
function autoSetUpdateUser(instance) {
  const user = getCurrentUser();
  if (user?.id) {
    instance.updatedBy = user.id;
  }
}

// 注册单实例操作钩子
sequelize.addHook('beforeCreate', autoSetCreateUser);
sequelize.addHook('beforeUpdate', autoSetUpdateUser);

// 注册批量操作钩子(Sequelize批量操作默认不触发单实例钩子,需要单独处理)
sequelize.addHook('beforeBulkCreate', (instances) => {
  const user = getCurrentUser();
  if (user?.id) {
    instances.forEach(instance => {
      instance.createdBy = user.id;
      instance.updatedBy = user.id;
    });
  }
});

sequelize.addHook('beforeBulkUpdate', (options) => {
  const user = getCurrentUser();
  if (user?.id) {
    // 批量更新是直接生成SQL执行,需要手动把updatedBy加到更新字段里
    options.fields = options.fields || [];
    if (!options.fields.includes('updatedBy')) options.fields.push('updatedBy');
    options.attributes = options.attributes || {};
    options.attributes.updatedBy = user.id;
  }
});

注意事项

  • 配置完成后所有调用model.create()/model.update()/model.bulkCreate()/model.bulkUpdate()的地方都不需要手动传createdBy、updatedBy字段,会自动注入,完全不需要修改现有业务代码。
  • 非请求场景(比如定时任务、初始化脚本)操作数据库时,可以手动调用runWithUser()指定系统用户,避免字段为空。
  • 如果需要在批量删除时记录操作人,可以按照beforeBulkUpdate的逻辑新增beforeBulkDestroy钩子即可。

内容的提问来源于stack exchange,提问作者Tiza ls2erch

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 21:24:26