在nest.js 中,是不是可以在参数装饰器中获取服务实例?

Posted

技术标签:

【中文标题】在nest.js 中,是不是可以在参数装饰器中获取服务实例?【英文标题】: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;
);

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

【问题讨论】:

【参考方案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 更简单,不需要使用装饰器...

以上是关于在nest.js 中,是不是可以在参数装饰器中获取服务实例?的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有守卫装饰器的情况下始终验证 JWT? (Nest.js + 护照)

装饰器中添加参数

装饰器中添加参数

有啥方法可以在不使用 Nest.js 中的 async/await 的情况下从数据库中获取数据?

nest.js学习

python包装函数在装饰器中接受参数