返回文章列表 →

Note

异常过滤器

nestjs的异常过滤器介绍

发布于 更新于

异常过滤器

NestJs存在内置的异常过滤器 ExceptionFilter ,但是为了满足需要,有时会自定义异常过滤器,如所有错误整理成固定结构,小项目通常用不到。所有异常过滤器都要实现泛型接口 ExceptionFilter<T> 。并且实现 catch(exception: T, host: ArgumentsHost) 方法。T 表示异常的类型。常见的统一过滤器可以:

  • 统一错误响应格式。
  • 记录未知异常和堆栈。
  • 隐藏数据库密码、SQL 等内部信息。
  • 增加 requestId、请求路径和时间。
  • 把数据库异常转换成业务错误码。
  • 区分开发环境与生产环境错误详情。
  • 上报日志或监控平台。

定义异常过滤器

http-exception.filter.ts

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
} from "@nestjs/common";
import { Request, Response } from "express";

// @Catch() 就是告诉NestJS "HttpExceptionFilter"是个异常过滤器
// @Catch(HttpException) 告诉 Nest 这个特定过滤器只查找 HttpException 类型的异常。@Catch() 装饰器可接受单个参数或以逗号分隔的列表
// @Catch() 装饰器的参数列表为空,可以捕获每一个未处理的异常(无论异常类型如何)
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    // 获取响应实例
    const response = ctx.getResponse<Response>();
    // 获取请求实例
    const request = ctx.getRequest<Request>();
    // 获取状态码
    const status = exception.getStatus();

    // 定义响应内容
    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

ArgumentsHost

是 NestJS 对当前处理器底层参数的跨平台统一抽象,让过滤器等框架组件能够访问 HTTP、WebSocket 和微服务异常上下文中的真实对象。简单来说就是它获取当前请求环境的异常请求/响应内容

❓为什么不直接把 request 和 response 传进来
因为 NestJS 不只支持普通 HTTP,它还支持其他环境,并且同一个异常过滤器机制可能运行在:HTTP REST 请求、WebSocket、消息RPC / 微服务消息、GraphQL 请求

异常类型的HTTP状态

异常 最终 HTTP 状态
BadRequestException 400
UnauthorizedException 401
ForbiddenException 403
NotFoundException 404
InternalServerErrorException 500
未处理的普通 Error 500
HttpException 所有 NestJS HTTP 异常,包括 400、401、403、404、500

DTO 中 class-validator 参数校验失败会报 BadRequestException` 异常

常见方法

getType()

获取当前上下文类型,主要用途就是编写跨 HTTP、RPC 和 WebSocket 环境的通用组件

const type = host.getType();
// 普通 NestJS 上下文中常见结果为:http/ws/rpc,即http异常/websocket异常/微服务异常
console.log(type);
switchToHttp()

取得 HTTP 环境的参数访问器,用于获取http环境下的异常上下文

// host: ArgumentsHost
const http = host.switchToHttp();

// 获取请求数据
const request = http.getRequest();
// 获取响应数据
const response = http.getResponse();
// 执行下一个中间件,nestjs中通常不使用
const next = http.getNext();
switchTows()

用于 WebSocket 环境

// host: ArgumentsHost
const ws = host.switchToWs();

// 当前 WebSocket 客户端或连接对象
const client = ws.getClient();
// 客户端发送的数据
const data = ws.getData();
// 当前事件模式或事件名称
const pattern = ws.getPattern();
switchToRpc()

用于 NestJS 微服务或 RPC 上下文

const rpc = host.switchToRpc();

// 消息的 payload
const data = rpc.getData();
// 当前微服务消息对应的传输层上下文对象
const context = rpc.getContext();
getArgs()

直接获取底层的完整参数数组,根据不同的环境获取不同的异常上下文 HTTP 环境下,可以粗略理解成:const [request, response, next] = host.getArgs();

ArgumentsHost 和 ExecutionContext 的区别
ArgumentsHost 只关注当前执行环境及其参数,用于异常过滤器。ExecutionContext 继承了 ArgumentsHost 并额外提供 getClass (获取当前 Controller 类)和 getHandler (获取当前路由),所以 ArgumentsHost 常用于守卫和拦截器

作用域

根据绑定位置的不同,异常过滤器的作用于不同

方法级

cats.controller.ts

@Post()
// 也可以传入实例@UseFilters(new HttpExceptionFilter()),但是更推荐传入类 ,传入类内存消耗低一些,因为 Nest 可以在整个模块中轻松复用相同类的实例
@UseFilters(HttpExceptionFilter)
async create(@Body() createCatDto: CreateCatDto) {
 throw new HttpException();
}

控制器级

仅作用于CatsController的路由

cats.controller.ts

@Controller()
@UseFilters(HttpExceptionFilter)
export class CatsController {}

全局

全局作用域的过滤器,它用于整个应用程序,作用于每个控制器和每个路由处理器。也可通过在任意模块中使用 APP_FILTER + useClass 进行全局注册。

main.ts

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  //  注册全局异常过滤器
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();