typescript - Guards 和循环依赖中的服务注入(inject)

标签 typescript nestjs

我目前正在为守卫中的服务注入(inject)而苦苦挣扎。

好吧,我做了一个 JwtAuthGuard,它需要我的 FirebaseServiceUserService

所以我认为创建一个 AuthGuardModule 会很有用,所以我只需要在具有 Controller 的模块中导入我的 AuthGuardModules >,使用我的守卫。

不幸的是,我的 UserModule 也需要这个守卫。 所以当我在我的 UserModule 中导入我的 AuthGuardModule 时,我得到一个 Circular Dependency Error 是很自然的。

然后,我在 Nest.js 中尝试了所有与 Circular Dependency Error 或多或少相关的事情,但无能为力。

有人可以告诉我如果:

  • 我想只为我的守卫制作一个模块是完全错误的吗?
  • 如果不是,我该如何修复我的Circular Dependency Error
  • 如果我误解了那部分,为什么它不对,最好的方法是什么?

JwtAuthGuard

@Injectable()
export class JwtAuthGuard implements CanActivate {
  private readonly logger = new Logger(JwtAuthGuard.name)

  constructor(
    private readonly firebaseService: FirebaseService,
    private readonly userService: UserService
  ) { }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = request.headers['authorization'];

    if (!token) {
      throw new UnauthorizedException
    }

    try {
      const firebaseUser = await this.firebaseService.getUserByToken(token)
      request.user = await this.userService.getByEmail(firebaseUser.email)
      return true
    } catch (error) {
      this.logger.error(this.canActivate.name, error)
      throw new UnauthorizedException
    }
  }
}

AuthGuardModule

@Module({
    imports: [UserModule],
    providers: [FirebaseService, JwtAuthGuard],
    exports: [JwtAuthGuard]
})

export class AuthGuardModule {}

编辑

好吧,NestJS discord 的 jmcdo29 向我解释说:

Your AuthGuardModule should export the UserModule and the FirebaseService. Guards, when used in the @UseGuards() decorator, are used in the context of the current controller's module, instead of being looked at as an Injectable provider

所以我这样做了,它确实解决了我最初的问题,但我还有另一个循环依赖问题。

首先,让我们恢复一下我的架构:

授权模块

  • 它处理注册路由,以及发送一些电子邮件的路由,如密码重置或电子邮件验证。
  • 当我调用 register 路由时,它需要 UserModule 在我的数据库中创建一个用户
@Module({
  imports: [
    AuthGuardModule
  ],
  controllers: [AuthController],
  providers: [AuthService, EmailService, FirebaseService],
})

export class AuthModule {}

文件模块

  • 它处理文件的上传和删除。
@Module({
    imports: [
        forwardRef(() => AuthGuardModule),
        BlobModule,
        MongooseModule.forFeature([{ name: File.name, schema: FileSchema }])
    ],
    controllers: [FileController],
    providers: [FileService],
    exports: [FileService],
})

export class FileModule {}

用户模块

  • 它处理所有用户路由。
  • 它需要 FileModule,在修改头像时检查头像是否存在(我只有一个路径可以更新我所有用户的个人数据)
  • 它还需要 AuthGuardModule 来使用我的 JwtAuthGuard
@Module({
    imports: [
      forwardRef(() => AuthGuardModule),
      forwardRef(() => FileModule),
      MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])
    ],
    controllers: [UserController],
    providers: [UserService, FirebaseService],
    exports: [UserService, FileModule]
  })

  export class UserModule {}

AuthGuardsModule

  • 它包含我的 JWT 检查守卫,然后我检查我的数据库中的用户是否链接到已连接的用户
  • 它需要 UserModule 来获取我的数据库中的用户
@Module({
    imports: [forwardRef(() => UserModule), FirebaseModule],
    providers: [JwtAuthGuard, RegisterAuthGuard],
    exports: [JwtAuthGuard, RegisterAuthGuard, FirebaseModule, UserModule]
})

export class AuthGuardModule {}

我可以使用 firebase 来存储我的用户头像并在前端进行,但我必须将它存储在 Azure Blob 存储中(这是强加给我的),所以 FileModule 会处理所有这些事情

所以,现在,感谢你们,我解决了我的 AuthGuardsModule -> UserModule 循环依赖,我有一个新的循环依赖问题:AuthGuardsModule -> UserModule -> FileModule

我在 FileModule 中设置了 AuthGuardsModule 的 forwardRef,但 nest 自然地告诉我要确保双向关系的每一侧都用“forwardRef()”装饰

所以我在 UserModule 中将 forwardRef 导入到我的 FileModule 中,但同样如此。

有什么想法吗?

最佳答案

从我的角度来看,您需要使用 @nestjs/common 包中的 forwardRef 方法,它负责在循环依赖发生时处理它.参见 Circular dependency - NestJS docs

需要考虑的一件事是您的 JwtAuthGuard 的位置。它最初是在 AuthModule 中还是在另一个共享模块中?

我假设守卫最初在您的 AuthModule 中(例如,假设 /src/auth/guards/auth-guard.ts)。
我还假设 FirebaseService 位于 FirebaseModule 中并从那里导出,并且 FirebaseModule 可注入(inject)不需要其他类取决于其他循环引用类/模块。 (在这种情况下,您需要通过在涉及的类/模块中添加更多 forwardRef 来指示 Nest 应如何处理它)

根据您提供的代码,您可以执行如下操作:

授权模块

@Module({
    imports: [forwardRef(() => UserModule), FirebaseModule],
    providers: [...yourProviders],
    exports: [...yourExportedInjectables]
})

export class AuthModule {}

FirebaseModule

@Module({
    imports: [...requiredImports],
    providers: [FirebaseService],
    exports: [FirebaseService]
})

export class FirebaseModule {}

用户模块

@Module({
    imports: [forwardRef(() => AuthModule)],
    providers: [...yourProviders],
    exports: [...yourExportedInjectables]
})

export class UserModule {}

注意:您还可以根据具体用例在服务层使用forwardRef
NB2:守卫可以在一个共享模块中分组,该模块可以导入到 AuthModule 中,这样您就不必指定 Nest 在每个模块之间转发引用,而只需引用 AuthModule 在使用给定守卫的模块中。

希望对你有帮助!
同样,我对您的架构了解不多,因此我提供了基于假设的代码示例。
不要犹豫,发表评论/提供更多上下文,以便我可以相应地编辑我的答案。

关于typescript - Guards 和循环依赖中的服务注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71778848/

相关文章:

typescript - 在 typescript 变量命名中使用表情符号

javascript - 如何使用 array.from 和 new set 方法返回多个值

javascript - 如何从 Controller 在nestjs应用程序中发送错误代码?

node.js - NestJS - 文件上传到微服务

node.js - 如何在 NestJS GraphQL 解析器中访问 Response 对象

visual-studio - Visual Studio : How to debug TypeScript in a shared project using IIS Express and cross-project references (no linking or duplicating files)

angular - 在构建 'Cannot find name ' 排除后出现 lodash 错误''

angular - 在 Ionic 3/4/5 中渲染 HTML 内容

typescript - Nest 无法解析 [ServiceName] 的依赖项

docker - Docker容器在生产服务器上失败