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

NestJS实体类内获取Request对象遇阻,审计字段赋值求助

NestJS + TypeORM 审计字段赋值问题解决

问题描述

开发NestJS小型项目时,需要为实体添加created_by、modified_by这类审计字段,已将用户信息存入Request对象,控制器中可正常获取,但在TypeORM实体的@BeforeInsert、@BeforeUpdate钩子中使用@Req注入Request对象时,得到的是undefined,无法完成用户ID赋值。

原尝试代码

import { Req } from "@nestjs/common";
import { Exclude } from "class-transformer";
import { BeforeInsert, BeforeUpdate, Column, CreateDateColumn, DeleteDateColumn, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";

export class BaseModel{
    @PrimaryGeneratedColumn('uuid')
    id:string

    @CreateDateColumn()
    created_at:Date

    @Column({default:null})
    created_by:string

    @UpdateDateColumn()
    modified_at:Date

    @Column({default:null})
    modified_by:string

    @DeleteDateColumn({select:false})
    @Exclude()
    deleted_at:Date

    @BeforeInsert()
    setAuditsBeforeInsert(@Req() req:any){
        this.created_by=req.currentUser.id;
        this.modified_by=req.currentUser.id;
    }

    @BeforeUpdate()
    setAuditsBeforeUpdate(@Req() req:any){
        this.modified_by=req.currentUser.id;
    }
} 

问题原因

TypeORM的实体生命周期钩子(如@BeforeInsert、@BeforeUpdate)并不处于NestJS的依赖注入上下文范围内,因此无法直接通过@Req装饰器注入Request对象,这就是钩子中req为undefined的核心原因。

解决方案

方案一:使用AsyncLocalStorage传递请求上下文(推荐)

这是NestJS官方推荐的跨层级传递请求上下文的方式,能在实体钩子中直接获取当前请求的用户信息。

  1. 创建请求上下文存储
    创建src/common/context/request.context.ts,用于保存和获取当前请求的用户信息:
import { AsyncLocalStorage } from 'async_hooks';

export interface RequestContext {
  userId: string;
}

export const requestContextStorage = new AsyncLocalStorage<RequestContext>();
  1. 实现请求上下文拦截器
    创建src/common/interceptors/request-context.interceptor.ts,在请求进入时将用户信息存入上下文存储:
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { requestContextStorage } from '../context/request.context';

@Injectable()
export class RequestContextInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const userId = request.currentUser?.id; // 对应你存入Request的用户ID字段

    return requestContextStorage.run({ userId }, () => next.handle());
  }
}
  1. 全局注册拦截器
    在app.module.ts中注册拦截器,让所有请求都经过上下文处理:
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { RequestContextInterceptor } from './common/interceptors/request-context.interceptor';

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: RequestContextInterceptor,
    },
  ],
})
export class AppModule {}
  1. 修改实体钩子逻辑
    更新BaseModel中的钩子,从上下文存储中获取用户ID:
import { Exclude } from "class-transformer";
import { BeforeInsert, BeforeUpdate, Column, CreateDateColumn, DeleteDateColumn, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";
import { requestContextStorage } from './common/context/request.context';

export class BaseModel{
    @PrimaryGeneratedColumn('uuid')
    id:string

    @CreateDateColumn()
    created_at:Date

    @Column({default:null})
    created_by:string

    @UpdateDateColumn()
    modified_at:Date

    @Column({default:null})
    modified_by:string

    @DeleteDateColumn({select:false})
    @Exclude()
    deleted_at:Date

    @BeforeInsert()
    setAuditsBeforeInsert(){
        const context = requestContextStorage.getStore();
        if (context) {
            this.created_by = context.userId;
            this.modified_by = context.userId;
        }
    }

    @BeforeUpdate()
    setAuditsBeforeUpdate(){
        const context = requestContextStorage.getStore();
        if (context) {
            this.modified_by = context.userId;
        }
    }
} 

方案二:使用自定义Repository(手动传递用户ID)

如果不想用AsyncLocalStorage,也可以通过自定义Repository,在Service层调用时手动传入用户ID。

  1. 创建基础Repository
import { Repository } from 'typeorm';

export class BaseRepository<T> extends Repository<T> {
  async createWithAudit(entity: Partial<T>, userId: string): Promise<T> {
    const newEntity = this.create({
      ...entity,
      created_by: userId,
      modified_by: userId,
    });
    return this.save(newEntity);
  }

  async updateWithAudit(id: string | number, entity: Partial<T>, userId: string): Promise<T> {
    await this.update(id, {
      ...entity,
      modified_by: userId,
    });
    return this.findOneBy({ id } as any);
  }
}
  1. 实体绑定自定义Repository
    在你的业务实体上指定使用这个基础Repository:
import { Entity } from 'typeorm';
import { BaseModel } from './base.model';
import { BaseRepository } from './base.repository';

@Entity({ repository: () => BaseRepository })
export class User extends BaseModel {
  // 你的业务字段
  @Column()
  username: string;
}
  1. Service层调用时传入用户ID
    在Service中从Request获取用户ID,调用自定义Repository的方法:
import { Injectable, Req } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './user.entity';
import { BaseRepository } from './base.repository';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: BaseRepository<User>,
  ) {}

  async createUser(@Req() req, userData: Partial<User>) {
    return this.userRepository.createWithAudit(userData, req.currentUser.id);
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 00:43:19