AsyncLocalStorage 不适用于每个请求

问题描述 投票:0回答:1

我在 NodeJS +16 中使用 NestJS 作为后端框架

我正在尝试实施: https://medium.com/@sascha.wolff/advanced-nestjs-how-to-have-access-to-the-current-user-in-every-service-without-request-scope-2586665741f

我的想法是拥有一个 @Injectable() 服务,除其他外,该服务将具有以下方法:

hasUserSomeStuff(){
const user = UserStorage.get()
if(user) {
// do magic
}

然后传递此服务,就像通常在 NestJS 中完成的那样

为了避免将请求传递到兔子洞,或冒泡请求范围,以便为每个请求实例化每个依赖项,但也要避免在需要从当前请求获取用户并执行操作的任何地方使用 UserStorage

我已经多次阅读文档,据我了解,节点将负责为每个异步上下文(在我的情况下为每个请求)实例化一个新存储,但在我身上发生的事情似乎是当我第一次运行时我的后端,它工作得很好,我从当前请求中获取了用户,但是一旦第一个异步上下文/承诺完成,我就检索了消费者的数据,并在下一个请求中 UserStorage 返回一个未定义的(如文档所述如果您位于同一异步上下文之外,则会出现这种情况,我想这不是发生的情况,因为它应该是一个全新的异步上下文)

但是,如果我调试,似乎会发生的是,在应用程序准备好使用之前,调用此 UserStorage 并在 init 处实例化一个新的 AsyncLocalStorage,然后第一个请求返回未定义的用户。

我不明白发生了什么事,任何人都可以帮助我,或者有更好的方法来实现我的目标吗?

提前致谢

javascript node.js nestjs async-hooks
1个回答
1
投票

首先我们需要了解 AsyncLocalStorage 的工作原理。 您将能够检索从 AsyncLocalStorage 实例显式执行的上下文中的任何信息。

例如:

//global scope 
const context = new AsyncLocalStorage()
//with typescript
//const context = new AsyncLocalStorage<UserPayload>()

async function test() {
  const userPayload = {
     id: 1,
     name: 'hiki9'
  }
  context.run(userPayload, () => whateverFunction()) 
}

async function whateverFunction() {
  const unknownPayload = context.getStore();
  //which is userPayload
}

whateverFunction
之后调用的每个函数都将共享上下文。

假设您使用的是 NestJS,以下是常见用法。

//author-context.service.ts
export interface AuthorContext {
  id: number
  name: string
}

@Injectable() 
export class AuthorUserContextService extends AsyncLocalStorage<AuthorContext>{}
//user.service.ts
@Injectable()
export class UserCreateService {
   constructor(private readonly context: AuthorUserContextService){}
   execute(dto: UserCreateDTO) {
      const store: AuthorContext = this.context.getStore()
      //const user = new User(store.whatever)
      //await this.userRepository.save(user)
      //inside userRepository, you can still `getStore`

   }
}

然后您需要在执行线程开始时

AsyncLocalStorage.run
。如果您正在使用 http 执行一些 api,只需将其绑定在控制器的开头即可。在这种情况下,使用 NestJS 会更好地配置一个
MiddlewareConsumer

//iam.module.ts
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';

@Module({
  providers: [
    AuthorUserContextService,
    UserCreateService,
  ]
})
export class IamModule{
  constructor(
    //whateverServiceThatYouNeed: WhateverServiceThatYouNeed
  ) {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply((req: Request, res: Response, next: NextFunction) => {
          const userId = request.headers['user-internal-id'] //it will depends 
          const store = {
             userId,
             requestBody: request.body,
             //... 
          }
          this.context.run(store, () => next());
       })    
       .forRoutes('*');
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.