我想在每次使用 sequelize 创建或更新时附加 userId。应该与默认时间戳类似。
我的数据库的每个模型都有一个字段 createdBy 和 updatedBy(引用 userId)。
我不想在每个 model.create() 或 model.update() 中附加用户数据,而是创建一个全局挂钩。
类似的东西:
sequelize.addHook('beforeCreate', populateCreatedBy);
但是我没有找到一种“简单”的方法来将来自我的中间件的 req.user 附加到 函数 populateCreatedBy().
我只找到了暗示在每个模型上调用一个钩子并将 req.user 传递给模型参数然后从另一个钩子中检索它的解决方案。但我发现它与在每个 model.create() 上传递该属性一样......
有什么方法可以将 req.user 传递给 sequelize.addHook('beforeCreate',populateCreatedBy())
提前致谢!
我不认为有一种方法可以在不调用每个模型的钩子的情况下执行此操作。相反,您可以尝试使用 session storage 来存储用户数据并使用
beforeUpdate()
或 beforeCreate()
挂钩更新模型中的用户数据。
全局钩子参考 - 全局钩子的 Sequelize 文档
我对会话存储的建议是快速会话 - 特快专场
在我的示例中使用 nestjs。所有这些都可以在 express 上完成。 创建中间件,然后定义以下全局钩子
//sequelize-attach-req-to-model.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { InjectConnection, } from "@nestjs/sequelize";
import { Sequelize, Model } from "sequelize-typescript";
interface ModelCustom extends Model {
request: Request
}
@Injectable()
export class SequelizeAttachReqToModelMiddleware implements NestMiddleware {
constructor(@InjectConnection() private readonly sequelize: Sequelize) {}
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
this.sequelize.beforeCreate((model: ModelCustom) => {
model.request = req
})
this.sequelize.beforeUpdate((model: ModelCustom, options) => {
model.request = req
})
this.sequelize.beforeDestroy((model: ModelCustom, options) => {
model.request = req
})
next();
}
}
//app.module.ts
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(SequelizeAttachReqToModelMiddleware)
.forRoutes('*');
}
}
接下来,在模型钩子中使用请求
//services.model.ts
@Table({ tableName: 'services' })
export class CardService
extends Model<InferAttributes<CardService>, InferCreationAttributes<CardService>>
implements ICardsService {
@ApiProperty({ example: 1, description: 'Уникальный индитефикатор' })
@Column({ type: DataType.INTEGER, unique: true, autoIncrement: true, primaryKey: true })
id: CreationOptional<number>
@ApiProperty({ example: 1, description: 'Название услуги' })
@Column({ type: DataType.STRING, unique: true })
name: string
@ApiProperty({ example: 1, description: 'Описание услуги' })
@Column({ type: DataType.STRING })
description: string
@AfterCreate
static afterCreateHook(instance, options: any) {
return saveAuditLog('create', instance, options);
}
@AfterUpdate
static afterUpdateHook(instance, options: any) {
return saveAuditLog('update', instance, options);
}
@AfterDestroy
static afterDestroyHook(instance, options: any) {
return saveAuditLog('destroy', instance, options)
}
}
async function saveAuditLog(action, model: ModelCustom, options) {
const userFromRequest: User = model.request.user as User
console.log(userFromRequest)
const auditLog = await AuditLog.create({
table_name: getOptions(model).tableName,
table_row_id: model.get('id'),
action: action,
timestamp: new Date(),
previous_values: model.previous(),
current_values: model.get(),
userId: userFromRequest.id
});
return auditLog
}
与 spl33t 类似,我通过在中间件函数中将全局挂钩附加到我的 sequelize 来解决这个问题,然后将其应用于我的路由器:
中间件:
import { Request, Response, NextFunction } from 'express';
import { sequelize } from '../models';
export default function (req: Request, res: Response, next: NextFunction) {
try {
sequelize.beforeUpdate('attachReqToModel', (model: any) => {
model.request = req;
});
return next();
} catch (e) {
console.error(e);
}
}
路线:
import attachReqToModelMiddleware from '../middleware/attachReqToModel.middleware';
router.use(attachReqToModelMiddleware);
型号:
@AfterUpdate
static exampleFunc(instance: any, options: any) {
try {
console.log(instance.request)
} catch (e) {
console.error(e);
}
}
要将实际用户附加到请求,您还应该使用在上述代码之前运行的中间件,在我的例子中,我使用的是存储用户 ID 的 JWT,在我的中间件中,我将其解码并将其附加到请求:
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import config from '../config';
export default function(req: Request, res: Response, next: NextFunction) {
const token = req.headers['authorization']?.split('Bearer ')[1];
try {
const jwtPayload = jwt.verify(token!, config.API_TOKEN_SECRET);
if (!jwtPayload?.sub) {
throw new Error('Missing or incorrect payload')
}
req.userId = Number(jwtPayload.sub);
return next();
}
catch (e) {
console.error(e);
return res.status(401).json({ message: 'Unauthorized' });
}
}
我的路线文件变成:
import authnMiddleware from '../middleware/authn.middleware';
import attachReqToModelMiddleware from '../middleware/attachReqToModel.middleware';
router.use(authnMiddleware);
router.use(attachReqToModelMiddleware);
关于挂钩顺序,请查看此链接。您可以使用
afterValidate
作为全局挂钩将请求附加到模型,然后您应该可以在 beforeCreate
中访问它。