Exception Filter

封装统一的 Exception Filter

Exception Filter 是在 Nest 应用抛异常的时候,捕获它并返回一个对应的响应。

Nest 给我们定义了很多 Exception,例如 HttpException,以及它的更多子 Exception:

  • BadRequestException
  • ForbiddenException
  • ...等等。

比较通用的是 HttpException,基于它我们可以指定想要的 error 信息以及状态码等。

typescript
1throw new HttpException('Forbidden', HttpStatus.FORBIDDEN)

如果我们不想要内置的 Exception Filter,想要自己处理 error 返回信息、状态码以及做更多的操作则可以自定义一个 Filter。

bash
1nest generate filter CustomError --no-spec
typescript
1import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common'
2
3@Catch()
4export class CustomErrorFilter<T> implements ExceptionFilter {
5  catch(exception: T, host: ArgumentsHost) {}
6}

@Catch装饰器可以指定需要捕捉的 Exception。

在这里我直接就指定需要捕捉 HttpException 错误(也可以不指定)。

typescript
1@Catch(HttpException)
2export class CustomErrorFilter implements ExceptionFilter {
3  catch(exception: HttpException, host: ArgumentsHost) {}
4}

接着在 Controller 中抛出一个错误:

typescript
1throw new BadRequestException('xxxxx')

然后再在CustomErrorFilter中打断点看一下抛出的错误:

image-20231118194650070

我们抛出的 message 跟默认的 error 信息和 statusCode 都在 exception 中被捕获到了。

这里只需要用 response 对象返回信息给前端即可:

typescript
1@Catch(HttpException)
2export class CustomErrorFilter implements ExceptionFilter {
3  catch(exception: HttpException, host: ArgumentsHost) {
4    const ctx = host.switchToHttp()
5    const response = ctx.getResponse()
6    const res = exception.getResponse()
7    const statusCode = exception.getStatus()
8    if (typeof res === 'string') {
9      response.status(statusCode).json({
10        url: ctx.getRequest().url,
11        message: res,
12        error: exception.name,
13        statusCode: exception.getStatus(),
14      })
15    } else {
16      response.status(statusCode).json({
17        url: ctx.getRequest().url,
18        ...res,
19      })
20    }
21  }
22}

如果想要在 Controller 中使用 Filter 就使用@UseFilters(CustomErrorFilter)装饰器。

如果想要 Global 使用,可以使用 APP_FILTER token 或者 app.useGlobalFilters()方法。

现在所有 HttpException 相关的错误信息都统一被处理掉了。

BadRequestException 返回信息:

image-20231118203040598

Pipe 校验错误返回信息:

image-20231118202951668

如果想要自定义 Exception,最好自继承 Nest 提供的 Exception。

下面是 BadRequestException 的源码,它就继承了 HttpException:

typescript
1export declare class BadRequestException extends HttpException {
2  constructor(objectOrError?: string | object | any, descriptionOrOptions?: string | HttpExceptionOptions)
3}

总结

  • 我们可以封装一个自定义的 Filter,通过 @Catch 指定要捕获的异常,然后在 catch 方法里拿到异常信息,返回对应的响应。
  • 如果捕获的是 HttpException,要注意兼容 response 为 object 或者 string 的处理。
  • filter 可以通过 @UseFilters 加在 handler 或者 controller 上,也可以在 main.ts 用 app.useGlobalFilters 全局启用。
  • 如果 filter 要注入其他 provider,就要通过 AppModule 里注册一个 token 为 APP_FILTER 的 provider 的方式。
  • 捕获的 Exception 可以自定义,最好继承自 Nest 提供的 Exception。