如何在 NestJS 控制器中注入请求范围的提供程序?

boe*_*bot 6 node.js typescript nestjs

我有一个请求范围的可注入日志记录。

import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST })
export class RequestLogger {
  public log(message: string) {
    console.log(message);
  }
}

Run Code Online (Sandbox Code Playgroud)

(暂时忽略它尚未在请求中使用构造函数;这不是重点)

我还有一个带有一些单例注入的控制器。

我想以允许控制器仅启动一次的方式注入可注入的请求。在构造函数中注入它将使控制器在每个请求上重新创建,所以肯定不是这样的(是吗?)。

我尝试将其放在方法签名中,但这似乎没有任何作用。

例如

@Controller('register')
export class RegisterApiController {
  public constructor(
    private readonly registerService: RegisterService,
  ) {
    console.log('Controller initiated');
  }

  @Post()
  public async postIndex(
    @Inject(RequestLogger) logger: RequestLogger,
  ): Promise<unknown> {
    console.log('request made');
    logger.log('Logger message to log');

    return this.registerService.register();
  }
}
Run Code Online (Sandbox Code Playgroud)

在应用程序引导之后(还包括“控制器启动”),每个请求都会以错误 500 终止,并在控制台中

Request made
[TypeError] Cannot read property 'log' of undefined
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以注入请求范围内的可注入项,而无需强制重新创建使用它的控制器?它是什么?

如果没有其他方法,是否至少有一种方法可以将初始控制器逻辑移动到其他地方,以便可以在那里完成第一个控制器初始化时需要执行的操作?

pro*_*fes 5

我最近创建的库可以实现这一点,它没有气泡注入链和性能问题:

https://github.com/kugacz/nj-request-scope

RequestScopeModule在模块类中注册后:

import { RequestScopeModule } from 'nj-request-scope';

@Module({
    imports: [RequestScopeModule],
})
Run Code Online (Sandbox Code Playgroud)

您的示例请求范围RequestLogger类将如下所示:

import { Injectable, Scope } from '@nestjs/common';
import { RequestScope } from 'nj-request-scope';

@Injectable()
@RequestScope()
export class RequestLogger {
  public log(message: string) {
    console.log(message);
  }
}
Run Code Online (Sandbox Code Playgroud)

并在您的示例控制器中添加类字段logger
logger字段将是RequestLogger为每个请求创建的新实例,而无需重新创建 RegisterApiController 的新实例。

@Controller('register')
export class RegisterApiController {
  public constructor(
    private readonly registerService: RegisterService,
    private readonly logger: RequestLogger,
  ) {
    console.log('Controller initiated');
  }

  @Post()
  public async postIndex(): Promise<unknown> {
    console.log('request made');
    logger.log('Logger message to log');

    return this.registerService.register();
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以在这里找到另一个示例: https: //github.com/kugacz/nj-request-scope-example/tree/main/src/request.scope

  • 我没有过多地研究代码,但如果它确实使用 Spring Proxy 方法,那么这是推荐的方法,没有问题。NestJS 真的让我很难过。我希望这是 JS 想要的 Spring。但它违背了 Spring 的一些最佳特性,例如: Injectables 并不是真正的代理,所以你不能在其中放置缓存或验证规则。它所拥有的只是 REST 控制器级别的拦截器层。我们都知道,控制器只是端口,而不是实际的 API,服务才是 API (2认同)

Kim*_*ern 4

不幸的是,对于nestjs,这是不可能的,请参阅文档

范围在注入链中起泡。依赖于请求范围的提供者的控制器本身也将是请求范围的。


但是,您可以将初始控制器逻辑作为单例注入到控制器中,这样就不会针对每个请求再次执行它。