在全局(单例)服务中使用特定于请求的上下文

Posted

技术标签:

【中文标题】在全局(单例)服务中使用特定于请求的上下文【英文标题】:Use request-specific context inside a global (singleton) service 【发布时间】:2020-01-06 11:13:47 【问题描述】:

我希望能够记录(如console.log)每个正在执行的 SQL 查询的登录用户。我正在使用 Nest.js 和 TypeORM(它是 Nest.js 的包装器)。我不知道如何将特定于请求的数据注入到全局(单例)提供程序中。

我正在使用 Nest.js v6.6.3 和 @nestjs/typeorm v6.1.3。

// current-user.service.ts
@Injectable( scope: Scope.REQUEST )
export class CurrentUserService 
  get currentUser(): string | null 
    if (!this.request || !this.request.user) 
      return null;
    

    return this.request.user.email;
  

  constructor(@Optional() @Inject(REQUEST) private request: Request) 


// logger.service.ts
@Injectable()
export class LoggerService extends Logger 
  constructor(private readonly moduleRef: ModuleRef) 
    super();
  

  log(message: any, context?: string) 
    super.log(this.formatMessage(message), context);
  

  private formatMessage(message: any) 
    // ** This is the problematic code **
    const currentUserService = this.moduleRef.get(CurrentUserService, 
      strict: false,
    );
    return `[$currentUserService.currentUser]: $message`;
  


// typeorm-logger.service.ts
@Injectable()
export class TypeormLogger implements Logger 
  // Logger is imported from `typeorm`
  constructor(private logger: LoggerService) 

  logQuery(query: string) 
    // there are more params, don't matter for the sake of the argument
    this.logger.log(query);
  


// logger.module.ts
@Module(
  providers: [LoggerService, TypeormLogger],
  exports: [LoggerService, TypeormLogger],
)
export class LoggerModule 

// app.module.ts
@Module(
  imports: [
    TypeOrmModule.ForRootAsync(
      imports: [LoggerModule],
      inject: [TypeormLogger],
      useFactory: (typeormLogger: TypeormLogger) => 
        return 
          type: 'mysql',
          logger: typeormLogger,
          keepConnectionAlive: false,
          ...
        ;
      ,
    ),
  ],
)
export class AppModule 

似乎使用moduleRef.get() 不能进行交叉Module 解析,所以我尝试使用moduleRef.resolve() 代替,但我仍然得到CurrentUserServicerequest === undefined。此外,resolve() 返回一个 Promise,这对于日志记录之类的东西并不理想。

以上是一个特定的用例,但可以概括 - 如果您的代码不是直接从控制器调用的,则不能注入像经过身份验证的用户或请求 id 这样的瞬态数据(两者都对日志记录很有用)。这看起来很奇怪,尤其是 Nest.js 严重依赖 IoC。

【问题讨论】:

这个运气好吗?我在同一条船上。 【参考方案1】:

我最终解决了 Nest.js,到目前为止它似乎运行良好。请注意,我们在内部应用程序中使用它,所以我们的并发用户数很少,所以 YMMV。

该解决方案基于asyncctx 包,正如here 建议的那样。你可以找到完整的解决方案here。

【讨论】:

@AgentLiquid - 见上文

以上是关于在全局(单例)服务中使用特定于请求的上下文的主要内容,如果未能解决你的问题,请参考以下文章

[][] 的含义是特定于上下文的吗?

servlet入门

如何在 Oracle EBS 中基于表的值集中使用描述性弹性域上下文

对于 CCV2 门户,有没有办法为 Spartacus / JS Storefront 指定特定于环境的属性?

Spring学习九 Servlet相关

Unity 中的单例每个调用上下文(Web 请求)