【问题标题】:In nest.js, is it possible to get service instance inside a param decorator?在nest.js 中,是否可以在参数装饰器中获取服务实例?
【发布时间】:2019-08-28 20:39:44
【问题描述】:

我想使用nest.js 实现这样的目标: (与 Spring 框架非常相似)

@Controller('/test')
class TestController {
  @Get()
  get(@Principal() principal: Principal) {

  }
}

阅读文档数小时后,我发现nest.js 支持创建自定义装饰器。所以我决定实现我自己的@Principal 装饰器。装饰器负责从 http 头中检索访问令牌,并使用令牌从我自己的身份验证服务中获取用户的主体。

import { createParamDecorator } from '@nestjs/common';

export const Principal = createParamDecorator((data: string, req) => {
  const bearerToken = req.header.Authorization;
  // parse.. and call my authService..
  // how to call my authService here?
  return null;
});

但问题是我不知道如何在装饰器处理程序中获取我的服务实例。可能吗?如何?提前谢谢你

【问题讨论】:

    标签: javascript node.js typescript decorator nestjs


    【解决方案1】:

    适用于 NestJS v7

    创建自定义管道

    // parse-token.pipe.ts
    import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
    import { AuthService } from './auth.service';
    
    @Injectable()
    export class ParseTokenPipe implements PipeTransform {
        // inject any dependency
        constructor(private authService: AuthService) {}
        
        async transform(value: any, metadata: ArgumentMetadata) {
            console.log('additional options', metadata.data);
            return this.authService.parse(value);
        }
    }
    

    将此管道与属性装饰器一起使用

    // decorators.ts
    import { createParamDecorator, ExecutionContext } from '@nestjs/common';
    import { ParseTokenPipe} from './parse-token.pipe';
    
    export const GetToken = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
      return ctx.switchToHttp().getRequest().header.Authorization;
    });
    
    export const Principal = (additionalOptions?: any) => GetToken(additionalOptions, ParseTokenPipe);
    

    在有或没有其他选项的情况下使用这个装饰器

    @Controller('/test')
    class TestController {
      @Get()
      get(@Principal({hello: "world"}) principal) {}
    }
    
    

    【讨论】:

      【解决方案2】:

      无法将服务注入自定义装饰器。

      相反,您可以创建一个AuthGuard 来访问您的服务。然后守卫可以向 request 对象添加一个属性,然后您可以使用自定义装饰器访问该属性:

      @Injectable()
      export class AuthGuard implements CanActivate {
        constructor(private authService: AuthService) {}
      
        async canActivate(context: ExecutionContext): Promise<boolean> {
          const request = context.switchToHttp().getRequest();
          const bearerToken = request.header.Authorization;
          const user = await this.authService.authenticate(bearerToken);
          request.principal = user;
          // If you want to allow the request even if auth fails, always return true
          return !!user;
        }
      }
      
      import { createParamDecorator } from '@nestjs/common';
      
      export const Principal = createParamDecorator((data: string, req) => {
        return req.principal;
      });
      

      然后在你的控制器中:

      @Get()
      @UseGuards(AuthGuard)
      get(@Principal() principal: Principal) {
        // ...
      }
      

      请注意,nest 提供了一些用于身份验证的标准模块,请参阅docs

      【讨论】:

        【解决方案3】:

        您可以对所有控制器使用中间件。

        auth.middleware.ts

        
        interface AccountData {
          accId: string;
          iat: number;
          exp: number;
        }
        
        interface RequestWithAccountId extends Request {
          accId: string;
        }
        
        @Injectable()
        export class AuthMiddleware implements NestMiddleware {
          constructor(private readonly authenticationService: AuthenticationService) {}
          async use(req: RequestWithAccountId, res: Response, next: NextFunction) {
            const token =
              req.body.token || req.query.token || req.headers['authorization'];
            if (!token) {
              throw new UnauthorizedException();
            }
            try {
              const {
                accId,
              }: AccountData = await this.authenticationService.verifyToken(token);
              req.accId = accId;
              next();
            } catch (err) {
              throw new UnauthorizedException();
            }
          }
        }
        
        

        然后创建 AccountId 装饰器

        account-id.decorator.ts

        import {
          createParamDecorator,
          ExecutionContext,
          UnauthorizedException,
        } from '@nestjs/common';
        
        export const AccountId = createParamDecorator(
          (data: unknown, ctx: ExecutionContext) => {
            const req = ctx.switchToHttp().getRequest();
            const token = req.accId;
            if (!token) {
              throw new UnauthorizedException();
            }
            return token;
          },
        );
        

        然后在你的控制器中应用 AccountId 装饰器

        your.controller.ts

          @Get()
          async someEndpoint(
            @AccountId() accountId,
          ) {
            console.log('accountId',accontId)
          }
        

        【讨论】:

        • 使用async someEndPoint (@Request() req) { return req.accId }更简单,不需要使用装饰器...
        猜你喜欢
        • 2019-02-05
        • 2019-11-11
        • 2021-03-28
        • 2018-01-21
        • 2019-02-01
        • 2018-08-08
        • 1970-01-01
        • 1970-01-01
        • 2022-08-19
        相关资源
        最近更新 更多