Я пытаюсь использовать 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)
Как я могу это исправить ?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


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;
}
}));
}
}
Попробуйте в этом кодыпесочница.
В качестве альтернативы вы можете использовать перехватчик, см. мое редактирование. Однако установка ответа не является чем-то необычным для фильтров исключений, это значение по умолчанию. См. docs.nestjs.com/исключение-фильтры
Я создал для вас живой пример: codeandbox.io/embed/…
Я не думаю, что это хороший совет, чтобы реализовать всю эту церемонию, когда классы исключений NestJS уже автоматически преобразованы в соответствующие ответы HTTP.
Конечно, прямой вызов исключения гнезда (или подкласса) является еще одним (очевидным?) вариантом. Но если у вас нет контроля над ошибками, например. MongoError, тогда это кажется мне жизнеспособным решением.
Поскольку ОП хотел (повторно) выбрать вариант гнезда с самого начала, я предположил, что они знали об этом.
Jemar показывает, как правильно «отобразить» встроенное исключение NestJS: stackoverflow.com/a/65473464/6764310
На основе решения Кима Керна я создал этот абстрактный класс
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 в нашем примере).
Ключевым моментом здесь является расширение 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. В зависимости от вашего запроса вы можете раскрыть больше, чем намеревались. В моем случае я просто отказался от каких-либо подробностей.
В порядке. Но кажется странным вручную строить тело ответа. Нет встроенного механизма для сопоставления исключений с ответами http? Я не думаю, что пытаюсь сделать что-то очень необычное