В nest.js возможно ли получить экземпляр службы внутри декоратора параметров?

#javascript #node.js #typescript #decorator #nestjs

#javascript #node.js #typescript #декоратор #nestjs

Вопрос:

Я хочу добиться чего-то подобного, используя nest.js : (что-то очень похожее на Spring framework)

 @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:

Невозможно внедрить службу в ваш пользовательский декоратор.

Вместо этого вы можете создать 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 предлагает некоторые стандартные модули для аутентификации, см. Документы.

Ответ №2:

для 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) {}
}

  

Ответ №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 decorator в вашем контроллере

your.controller.ts

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

Комментарии:

1. использование async someEndPoint (@Request() req) { return req.accId } is намного проще, нет необходимости использовать декоратор…