请求范围的服务
Posted
技术标签:
【中文标题】请求范围的服务【英文标题】:Request-Scoped Services 【发布时间】:2019-04-26 06:23:10 【问题描述】:据我所知,nestjs 在应用程序启动时会创建所有服务,也许我用错了有没有办法配置 NestJs 以根据请求创建服务?就像每次完成请求时,都会重新创建一些用于该请求的服务?
【问题讨论】:
【参考方案1】:随着nest.js 6.0 的发布,添加了injection scopes。有了这个,您可以为您的提供商选择以下三个范围之一:
SINGLETON:默认行为。您的提供程序的一个实例用于整个应用程序 TRANSIENT:为注入它的每个提供程序创建一个专用的提供程序实例。 REQUEST:对于每个请求,都会创建一个新的提供者。⚠️ 注意:注入作用域会在你的依赖链中冒泡。 ⚠️
示例:如果UsersController
(单例范围)注入UsersService
(单例范围)再注入OtherService
(请求范围),那么UsersController
和UsersService
都将自动变为请求范围。
用法
将其添加到 @Injectable()
装饰器:
@Injectable( scope: Scope.REQUEST )
export class UsersService
或者在你的模块定义中为自定义提供者设置它:
provide: 'CACHE_MANAGER',
useClass: CacheManager,
scope: Scope.TRANSIENT,
【讨论】:
注意 - 在当前版本中,Scope.REQUEST
服务不适用于 nest/cqrs
命令和查询
我猜瞬态作用域也会在注入链中冒泡,不是吗?
@f***ski 是的,它也会冒泡。对性能的影响可以忽略不计,因为实例化是在启动时完成的,而不是在 每个 请求上完成的。不过我会改写,这还不清楚。【参考方案2】:
1 月 19 日更新
这将在 nest.js 6.0 中成为可能,请参阅pull request。
正如卡米尔在issue 中所写:
一旦 async-hooks 功能(它在节点 10 中仍处于试验阶段)是 稳定,我们会考虑提供一个内置的解决方案 请求范围的实例。
所以目前,这似乎还不可能。您只能将整个模块标记为 SingleScope
而不是默认的单例行为。使用SingleScope
,将为每次导入创建一个新的模块实例。
【讨论】:
【参考方案3】:我想使用 di 来标记整个端点以在 事务,而无需为 端点。而不是使用 di 有一个包可以帮助我们 基于名为
cls-hooked
的回调链创建资源 是基于 Kim Kern 在他的回复中写的,async-hooks
使用 这我能够在调用 enpoint 时启动事务,并且 一旦 enpoint 完成就停止它,因为我使用typeorm
作为 db orm 我 能够使用this solution
【讨论】:
【参考方案4】:可以使用内置的请求范围依赖注入机制 https://docs.nestjs.com/fundamentals/injection-scopes 但根据文档,它有严重的缺点:
Scope 在注入链上冒泡。依赖于请求范围提供程序的控制器本身将是请求范围的。
使用请求范围的提供程序将对应用程序性能产生影响。虽然 Nest 尝试缓存尽可能多的元数据,但它仍然必须在每个请求上创建您的类的实例。因此,它会减慢您的平均响应时间和整体基准测试结果。除非提供程序必须是请求范围的,否则强烈建议您使用默认的单例范围。
最近我为 NestJS 创建了请求范围的实现,避免了注入链的冒泡和性能影响。
https://github.com/kugacz/nj-request-scope
要首先使用它,您必须在模块类装饰器中添加 RequestScopeModule 的导入:
import RequestScopeModule from 'nj-request-scope';
@Module(
imports: [RequestScopeModule],
)
接下来,request-scope注入有两种方式:
-
使用 NJRS_REQUEST 令牌将 express 请求对象注入到类构造函数中:
import NJRS_REQUEST from 'nj-request-scope';
[...]
constructor(@Inject(NJRS_REQUEST) private readonly request: Request)
-
使用 @RequestScope() 装饰器将类注入范围更改为请求范围:
import RequestScope from 'nj-request-scope';
@Injectable()
@RequestScope()
export class RequestScopeService
您可以在此存储库中找到示例实现:https://github.com/kugacz/nj-request-scope-example NestJS what is the best practice to initialise and pass request context 我想你可以使用我写的 nj-request-scope 包。它没有气泡注入链和性能问题:
https://github.com/kugacz/nj-request-scope
首先,您必须在模块类装饰器中添加RequestScopeModule
的导入:
import RequestScopeModule from 'nj-request-scope';
@Module(
imports: [RequestScopeModule],
)
然后你的RequestContext
类可以这样写:
import Injectable from '@nestjs/common';
import Request from 'express';
import IncomingHttpHeaders from 'http';
import RequestScope from 'nj-request-scope';
import NJRS_REQUEST from 'nj-request-scope';
@Injectable()
@RequestScope()
export class RequestContext
public headers: IncomingHttpHeaders;
....
constructor(@Inject(NJRS_REQUEST) private readonly request: Request)
this.headers = request.headers;
在这种情况下,将为每个请求创建一个 RequestContext
类的新实例。因此,您可以在此类中存储任何请求范围信息。将此类注入应用程序的任何部分都不会导致注入链冒泡。
另一种方法是这样的:
import Injectable from '@nestjs/common';
import Request from 'express';
import IncomingHttpHeaders from 'http';
import NJRS_REQUEST from 'nj-request-scope';
@Injectable()
export class RequestContext
constructor(@Inject(NJRS_REQUEST) private readonly request: Request)
public get headers(): IncomingHttpHeaders
return this.request.headers;
在这种情况下,RequestContext
类是单例范围,只有 request
字段是请求范围。因此,您不能将任何请求范围的信息直接存储在 RequestContext
类中,而必须通过 getter 直接从请求对象中读取请求范围的数据。此外,将此类注入应用程序的任何部分都不会导致注入链冒泡。
简单的 nj-request-scope 包使用示例,您可以在这里找到:https://github.com/kugacz/nj-request-scope-example
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review以上是关于请求范围的服务的主要内容,如果未能解决你的问题,请参考以下文章
Google API 授权(服务帐户)错误:HttpAccessTokenRefreshError:未授权客户端:请求中的未授权客户端或范围