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

ben*_*ngr 6 node.js express typeorm nestjs

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

我使用的是 Nest.js v6.6.3 和@nestjs/typeormv6.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 {}
Run Code Online (Sandbox Code Playgroud)

似乎 usingmoduleRef.get()不能进行交叉Module解析,所以我尝试使用moduleRef.resolve(),但后来我仍然CurrentUserService使用request === undefined. 此外,resolve()返回 a Promise,这对于日志记录之类的东西来说并不理想。

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

ben*_*ngr 0

我最终围绕 Nest.js 工作,到目前为止它似乎工作得很好。请注意,我们在内部应用程序中使用它,因此我们的并发用户数较低,所以 YMMV。

该解决方案基于该asyncctx包,如此处建议的那样您可以在这里找到完整的解决方案。