【问题标题】:Inject service into guard in Nest.JS在 Nest.JS 中将服务注入警卫
【发布时间】:2019-03-22 14:11:32
【问题描述】:

我有 KeysModule,可用于添加或删除 API 密钥。我需要这些密钥来保护某些路由免受未经授权的访问。 为了保护这些路由,我创建了 ApiGuard:

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

@Injectable()
export class ApiGuard implements CanActivate {

async canActivate(
    context: ExecutionContext,
  ): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    return request.headers.api_key;
  }
}

然后我在路由中使用它:

 @Get('/protected')
 @UseGuards(ApiGuard)
 async protected(@Headers() headers: Api) {
   const key = await this.ks.findKey({ key: headers.api_key });
   if (!key || !key.active) return 'Invalid Key';
   return 'Your API key works';
 }

其中 ks 是用于检查密钥是否正确的 KeyService。 这个解决方案有效,但很愚蠢。我必须在任何我想使用这个守卫的地方复制和粘贴一些代码行(我的意思是路线中的行)。

我尝试将所有逻辑移至 ApiGuard,但出现错误,无法将 KeyService 注入 ApiGuard 类。说明一下,我在 KeysModule 的 provider 中有 KeyService,但是 ApiGuard 是全局使用的。

你知道怎么做吗?

【问题讨论】:

    标签: node.js express nestjs


    【解决方案1】:

    从 NestJS v8 开始,似乎在接受的答案中注入 zsoca 回答的服务不再起作用。

    NestJS 8 的有效解决方案是提供类引用而不是字符串:

      constructor(@Inject(KeyService) private keyService: KeyService) {}
    

    【讨论】:

      【解决方案2】:

      守卫中使用的服务必须在其模块中导出。提供服务还不够!

      【讨论】:

        【解决方案3】:

        在guard中注入服务。你可以创建一个全局模块。

        // ApiModule
        import {Module,Global} from '@nestjs/common';
        import {KeyService} from '../';
        
        @Global()
        @Module({
            providers: [ KeyService ],
            exports: [KeyService]
        })
        export class ApiModule {}
        

        然后像这样将服务注入到守卫中

        // guard
        export class ApiGuard implements CanActivate {
        constructor(@Inject('KeyService') private readonly KeyService) {}
        }
         async canActivate(context: ExecutionContext) {
            // your code
            throw new ForbiddenException();
          }
        

        现在问题可以解决了。但是我还有一个问题。我想向服务中注入一些东西但是得到了这个错误:

        Nest 无法解析 AuthGuard (?, +) 的依赖关系。请确保索引 [0] 处的参数在当前上下文中可用。

        这是我的解决方案:

        在 KeyService 中注入其他依赖,比如nestjs docs 说。

        从任何模块外部注册的全局守卫(使用上面示例中的 useGlobalGuards())不能注入依赖项,因为这是在任何模块的上下文之外完成的。

        这是他们的样本:

        // app.module.js
        import { Module } from '@nestjs/common';
        import { APP_GUARD } from '@nestjs/core';
        
        @Module({
          providers: [
            {
              provide: APP_GUARD,
              useClass: RolesGuard,
            },
          ],
        })
        export class ApplicationModule {}
        

        它成功了。现在我可以使用保护全局而不会出现依赖错误。

        【讨论】:

        • 你知道在模块上设置保护的方法,但在特定控制器上
        【解决方案4】:

        也许为时已晚,但我遇到了同样的问题并找到了解决方案。也许有更好的,但它对我来说工作正常:

        将 KeysModule 定义为全局模块,你可以在 nestjs 文档中查看如何做到这一点:https://docs.nestjs.com/modules

        之后你可以这样做:

        import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
        
        @Injectable()
        export class ApiGuard implements CanActivate {
        
        constructor(
        @Inject('KeyService')
        private readonly ks
        ) {}
        
        const key = await this.ks.findKey();
        
        "YOUR_CODE_HERE..."
        
        }
        

        希望它对您或将来会坚持这一点的人有所帮助。

        【讨论】:

          【解决方案5】:

          您可以像在任何带有Injectable 注释的对象中一样,在防护中注入服务。 如果您的 ApiGuard 需要 KeyService,您有两种选择:

          • 在导入 KeysModule 的模块中添加 ApiGuard。然后导入创建的模块以全局使用 ApiGuard
          • 在 KeysModule 中添加 ApiGuard 并导出。

          【讨论】:

            猜你喜欢
            • 2022-01-19
            • 2021-05-22
            • 2018-08-16
            • 2021-04-05
            • 1970-01-01
            • 1970-01-01
            • 2020-08-03
            • 1970-01-01
            • 2016-05-09
            相关资源
            最近更新 更多