NestJS 从 ExceptionFilter 抛出
NestJS throw from 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)
我该如何解决这个问题?
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;
}
}));
}
}
在此 codesandbox 中尝试一下。
基于 Kim Kern 解决方案,我创建了这个摘要 class
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 的异常版本 classes,它已经随 NestJS 一起提供,这似乎很奇怪。默认情况下,这些将自动转换为具有正确错误代码的 HTTP 响应。您正在使用拦截器和抽象 class 实现增加开销,而您可以抛出 NestJS 错误并免费获得它。这是您所指的内置机制。
throw new BadRequestException('you done goofed');
结果:
{"statusCode":400,"error":"Bad Request","message":"you done goofed"}
这里的关键是扩展 BaseExceptionFilter
并委托给超级 class 而不是抛出:
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
}
这将导致您在抛出相同错误时收到的默认错误处理。
我尝试使用 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)
我该如何解决这个问题?
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;
}
}));
}
}
在此 codesandbox 中尝试一下。
基于 Kim Kern 解决方案,我创建了这个摘要 class
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 的异常版本 classes,它已经随 NestJS 一起提供,这似乎很奇怪。默认情况下,这些将自动转换为具有正确错误代码的 HTTP 响应。您正在使用拦截器和抽象 class 实现增加开销,而您可以抛出 NestJS 错误并免费获得它。这是您所指的内置机制。
throw new BadRequestException('you done goofed');
结果:
{"statusCode":400,"error":"Bad Request","message":"you done goofed"}
这里的关键是扩展 BaseExceptionFilter
并委托给超级 class 而不是抛出:
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
}
这将导致您在抛出相同错误时收到的默认错误处理。