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

Я хочу добиться чего-то подобного с помощью 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;
});

Но проблема в том, что я понятия не имею, как получить мой экземпляр службы внутри обработчика декоратора. Является ли это возможным? И как? заранее спасибо

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
11
0
8 342
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

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

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

Вы можете использовать middlewar для всех контроллеров.

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

аккаунт-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 в своем контроллере.

ваш.controller.ts

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

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

rickster 09.08.2021 09:20

для 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.info('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) {}
}

Другие вопросы по теме