В NestJS, как получить контекст выполнения или экземпляр запроса в декораторе пользовательского метода?

#decorator #nestjs

#декоратор #nestjs

Вопрос:

У меня есть пользовательский декоратор метода, подобный этому.

 export function CustomDecorator() {

    return applyDecorators(
        UseGuards(JwtAuthGuard)
    );
}
  

Внутри пользовательского декоратора я хочу получить заголовок запроса, но не уверен, как получить экземпляр запроса?

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

1. Не могли бы вы добавить больше информации о том, что вы хотите сделать, пожалуйста? Я понял, что вы хотите получить доступ к заголовкам, но с какой целью?

2. У нас есть общий модуль аутентификации, в котором мы можем использовать JWT или GoogleAuth. Я хочу реализовать пользовательский декоратор, который применяет различные средства защиты на основе заголовков запроса.

Ответ №1:

Вы не сможете получить ExectuionContext объект или Request object в декораторе класса или метода, потому что эти декораторы запускаются немедленно в момент импорта. Что следует сделать вместо этого, так это создать, SuperGuard который имеет ExecutionContext доступный для него. В это SuperGuard должны быть введены все другие средства защиты через constructor , и в зависимости от заголовка вы должны вызвать / вернуть результат из вызванного средства защиты. Что-то вроде этого:

 @Injectable()
export class SuperGuard implements CanActivate {
  constructor(
    private readonly jwtAuthGuard: JwtAuthGuard,
    private readonly googleAuthGuard: GoogleAuthGuard,
  ) {}

  canActivate(context: ExecutionContext) {
    const req = context.switchToHttp().getRequest();
    if (req.headers['whatever'] === 'google') {
      return this.googleAuthGuard.canActivate(context);
    } else {
      return this.jwtAuthGuard.canActivate(context);
    }
  }
}
  

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

1. Привет, Джей, я получаю эту ошибку. Ошибка: Nest не может разрешить зависимости RomeGuard (?). Пожалуйста, убедитесь, что аргумент JwtAuthGuard с индексом [0] доступен в контексте AuthModule. Возможные решения: — Если JwtAuthGuard является поставщиком, является ли он частью текущего AuthModule? — Если JwtAuthGuard экспортируется из отдельного @Module, импортируется ли этот модуль в AuthModule? @Module({ импортирует: [ /* модуль, содержащий JwtAuthGuard */ ] }) в инжекторе. lookupComponentInParentModules

2. Вам нужно убедиться, что везде, где вы используете, SuperGuard у вас есть JwtAuthGuard и GoogleAuthGuard добавлены как providers . Это можно сделать из GuardModule , если вы того пожелаете

3. Привет, Джей, я добавил это вот так providers: [SharedDataAuthService, JwtStrategy, JwtAuthGuard], , но он показывает эту ошибку.

4. Всего хорошего, Джей. Мне также нужно добавить это в exports. Спасибо за вашу помощь.

5. Привет, Джей, у меня вопрос… Если декоратор Guard запускается немедленно в момент импорта, как он может перехватить запрос в ExecutionContext ? ^^

Ответ №2:

Мне удалось получить доступ к контексту выполнения в декораторе, используя Inject внутри фабрики декоратора. Вот мой декоратор, который проглатывает ошибки, создаваемые методом, и возвращает предопределенное значение в случае исключения.

 import { Injectable, Scope, Inject, ExecutionContext } from '@nestjs/common';
import { CONTEXT } from '@nestjs/graphql';

@Injectable({ scope: Scope.REQUEST })
export class ExceptionsHandler {
  public constructor(@Inject(CONTEXT) private readonly context: ExecutionContext) {}

  private integrationsRequestErrors: unknown[] = [];

  public handle(error: unknown): void {
    // ADD error to context if necessary
    this.integrationsRequestErrors.push(error);
  }
}

export const ErrorSwallower = (options: {
  serviceImplementation: string;
  defaultValue: unknown;
  errorMessage?: string;
}): MethodDecorator => {
  const { defaultValue, integration } = options;
  const Injector = Inject(ExceptionsHandler);
  return (target: object, _propertyKey: string, descriptor: PropertyDescriptor) => {
    Injector(target, 'exceptionsHandler');
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: unknown[]) {
      const exceptionHandler = this.experiment as ExceptionsHandler;
      try {
        const result = originalMethod.apply(this, args);
        if (result amp;amp; result instanceof Promise) {
          return result.catch((error: unknown) => {
            exceptionHandler.handle({ error, integration });
            return defaultValue;
          });
        }
        return resu<
      } catch (error) {
        exceptionHandler.handle({ error, integration });
        return defaultValue;
      }
    };
  };
};
  

и вот приведенный выше код приведен в действие:

 @Injectable()
export class ExampleService {
  @ErrorSwallower({ serviceImplementation: 'ExampleClass', defaultValue: [] })
  private async getSomeData(args: IGetSomeDataArgs): Promise<ISomeData[]> {
    throw new Error('Oops');
  }
}