Выброс NestJS из ExceptionFilter

Я пытаюсь использовать ExceptionFilter для сопоставления исключений с их аналогом HTTP.

Это мой код:

@Catch(EntityNotFoundError)
export class EntityNotFoundFilter implements ExceptionFilter {
    catch(exception: EntityNotFoundError, _host: ArgumentsHost) {
        throw new NotFoundException(exception.message);
    }
}

Но при выполнении кода фильтра я получил UnhandledPromiseRejectionWarning

 (node:3065) UnhandledPromiseRejectionWarning: Error: [object Object]
    at EntityNotFoundFilter.catch ([...]/errors.ts:32:15)
    at ExceptionsHandler.invokeCustomFilters ([...]/node_modules/@nestjs/core/exceptions/exceptions-handler.js:49:26)
     at ExceptionsHandler.next ([...]/node_modules/@nestjs/core/exceptions/exceptions-handler.js:13:18)
     at [...]/node_modules/@nestjs/core/router/router-proxy.js:12:35
     at <anonymous>
     at process._tickCallback (internal/process/next_tick.js:182:7)
 (node:3065) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 5)

Как я могу это исправить ?

Поведение ключевого слова "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) для оценки ваших знаний,...
9
0
15 681
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

ExceptionFilter всегда является последним местом, которое вызывается перед отправкой ответа, оно отвечает за построение ответа. Вы не можете повторно создать исключение из ExceptionFilter.

@Catch(EntityNotFoundError)
export class EntityNotFoundFilter implements ExceptionFilter {
  catch(exception: EntityNotFoundError, host: ArgumentsHost) {
    const response = host.switchToHttp().getResponse();
      response.status(404).json({ message: exception.message });
  }
}

В качестве альтернативы вы можете создать Interceptor, который преобразует ваши ошибки:

@Injectable()
export class NotFoundInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    // next.handle() is an Observable of the controller's result value
    return next.handle()
      .pipe(catchError(error => {
        if (error instanceof EntityNotFoundError) {
          throw new NotFoundException(error.message);
        } else {
          throw error;
        }
      }));
  }
}

Попробуйте в этом кодыпесочница.

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

Varkal 01.03.2019 14:53

В качестве альтернативы вы можете использовать перехватчик, см. мое редактирование. Однако установка ответа не является чем-то необычным для фильтров исключений, это значение по умолчанию. См. docs.nestjs.com/исключение-фильтры

Kim Kern 01.03.2019 14:59

Я создал для вас живой пример: codeandbox.io/embed/…

Kim Kern 01.03.2019 15:19

Я не думаю, что это хороший совет, чтобы реализовать всю эту церемонию, когда классы исключений NestJS уже автоматически преобразованы в соответствующие ответы HTTP.

Jesse Carter 01.03.2019 18:43

Конечно, прямой вызов исключения гнезда (или подкласса) является еще одним (очевидным?) вариантом. Но если у вас нет контроля над ошибками, например. MongoError, тогда это кажется мне жизнеспособным решением.

Kim Kern 01.03.2019 19:00

Поскольку ОП хотел (повторно) выбрать вариант гнезда с самого начала, я предположил, что они знали об этом.

Kim Kern 01.03.2019 19:02

Jemar показывает, как правильно «отобразить» встроенное исключение NestJS: stackoverflow.com/a/65473464/6764310

KiwiKilian 12.05.2021 20:39

На основе решения Кима Керна я создал этот абстрактный класс

export abstract class AbstractErrorInterceptor<T> implements NestInterceptor {
    protected interceptedType: new (...args) => T;

    intercept(
        context: ExecutionContext,
        call$: Observable<any>,
    ): Observable<any> | Promise<Observable<any>> {
        return call$.pipe(
            catchError(exception => {
                if (exception instanceof this.interceptedType) {
                    this.handleError(exception);
                }
                throw exception;
            }),
        );
    }

    abstract handleError(exception: T);
}

И некоторые реализации

export class EntityNotFoundFilter extends AbstractErrorInterceptor<EntityNotFoundError> {
    interceptedType = EntityNotFoundError;

    handleError(exception: EntityNotFoundError) {
        throw new NotFoundException(exception.message);
    }
}

Кажется странным, что вы создаете свою собственную версию классов исключений на основе HTTP, которые уже поставляются с NestJS. По умолчанию они будут автоматически преобразованы в ответы HTTP с правильными кодами ошибок. Вы добавляете накладные расходы с помощью перехватчиков и реализаций абстрактных классов, когда вместо этого вы можете просто выдавать ошибки NestJS и получать их бесплатно. Это встроенный механизм, о котором вы говорили.

throw new BadRequestException('you done goofed');

приводит к:

{"statusCode":400,"error":"Bad Request","message":"you done goofed"}

Codesandbox (адаптировано от Кима)

Я не создаю свою собственную версию классов исключений на основе http: я хочу их использовать! Но я не хочу явно генерировать исключение на основе http из бизнес-кода, потому что эту логику можно повторно использовать в контексте, отличном от http (например, веб-сокет). Я хочу создать бизнес-исключение (например, UnderAgeException, если я проверяю возраст пользователя для какой-либо функции), а затем сопоставить их с их аналогом Http (BadRequestException в нашем примере).

Varkal 01.03.2019 20:56

Ключевым моментом здесь является расширение BaseExceptionFilter и делегирование суперклассу, а не выбрасывание:

import { BaseExceptionFilter } from '@nestjs/core';
// .. your other imports

@Catch(EntityNotFoundError)
export class EntityNotFoundFilter extends BaseExceptionFilter {
    catch(exception: EntityNotFoundError, host: ArgumentsHost) {
        super.catch(new NotFoundException(exception.message, host));
    }
}

Обязательно передайте аргумент applicationRef при создании фильтра во время начальной загрузки приложения, потому что BaseExceptionFilter необходимо, чтобы это свойство работало правильно.

import { HttpAdapterHost } from '@nestjs/core';
// .. your other imports

async function bootstrap(): Promise<void> {
  // .. blah blah
  const { httpAdapter } = app.get(HttpAdapterHost);
  app.useGlobalFilters(new GeneralErrorFilter(httpAdapter));
  // .. blah blah
}

Это приведет к обработке ошибок по умолчанию, которую вы получили бы, если бы вы выдали ту же ошибку.

Мне очень нравится это решение, потому что оно использует NestJS NotFoundException. Но я бы рекомендовал быть осторожным с пересылкой exception.message. В зависимости от вашего запроса вы можете раскрыть больше, чем намеревались. В моем случае я просто отказался от каких-либо подробностей.

KiwiKilian 12.05.2021 20:45

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