Nest: 掌握异常过滤器的艺术

创建项目,抛出异常

创建 Nest.js 项目:

bash 复制代码
nest new exception-filter-test -p pnpm

然后启动项目:

bash 复制代码
pnpm run start:dev

通过浏览器访问 http://localhost:3000,如果看到"hello world",说明服务已启动。

在控制器中,我们可以抛出 HttpException 错误:

HttpStatus 提供了各种状态码的常量。

访问页面:

这是由 Nest.js 的内置异常过滤器返回的。

创建异常过滤器

bash 复制代码
nest g filter httpError --flat --no-spec

--flat 参数表示不创建子目录,--no-spec 表示不生成测试文件。

自定义异常过滤器是通过实现 ExceptionFilter 接口来创建的,它必须实现 catch 方法:

使用 @Catch 装饰器来指定要捕获的异常类型。例如,下面的过滤器专门捕获 BadRequestException:

很多错误类型都是 HttpException 的子类,如果我们想捕获其错误,@Catch(HttpException) 就可以:

typescript 复制代码
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';
import { Response } from 'express';

@Catch(HttpException)
export class HttpErrorFilter<T> implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const http = host.switchToHttp();
    const request = http.getRequest();
    const response = http.getResponse<Response>();
    const res = exception.getResponse() as { message: string[] };
    const statusCode = exception.getStatus();

    response.status(statusCode).json({
      code: statusCode,
      // 兼容 ValidationPipe 的 res,如果res.message是数组则将其元素用逗号连接,否则直接使用异常的message
      message: res?.message?.join ? res?.message?.join(',') : exception.message,
      path: request.url,
    });
  }
}

访问页面:

以下是一些 HttpException 的常见子类:

  • BadRequestException:当客户端发送了一个错误的请求时使用,通常与状态码 400 一起使用。
  • UnauthorizedException:当请求需要用户认证时使用,通常与状态码 401 一起使用。
  • ForbiddenException:当用户认证成功,但没有足够的权限来访问资源时使用,通常与状态码 403 一起使用。
  • NotFoundException:当请求的资源不存在时使用,通常与状态码 404 一起使用。
  • NotAcceptableException:当服务器无法满足客户端请求的接受头中的条件时使用,通常与状态码 406 一起使用。
  • ConflictException:当请求的资源在当前状态下无法完成时使用,通常与状态码 409 一起使用。
  • GoneException:当请求的资源已被永久删除且没有转发地址时使用,通常与状态码 410 一起使用。
  • InternalServerErrorException:当服务器遇到意外情况,阻止它完成请求时使用,通常与状态码 500 一起使用。
  • NotImplementedException:当服务器不支持请求的功能时使用,通常与状态码 501 一起使用。
  • ServiceUnavailableException:当服务器暂时不可用,通常是由于过载或维护时使用,通常与状态码 503 一起使用。

当然,也可以 extends HttpException 自己扩展。

创建自定义异常过滤器

首先,我们定义一个自定义异常 UnLoginException:

typescript 复制代码
// src/unlogin.exception.ts

export class UnLoginException {
  message: string;

  constructor(message?: string) {
    this.message = message;
  }
}

这个异常类可以用来表示未登录或未授权的情况。

接下来,我们创建一个异常过滤器 UnloginFilter 来捕获 UnLoginException

bash 复制代码
nest g filter unlogin --flat --no-spec

内容如下:

typescript 复制代码
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpStatus,
} from '@nestjs/common';
import { Response } from 'express';
import { UnLoginException } from './unlogin.exception';

@Catch(UnLoginException)
export class UnloginFilter implements ExceptionFilter {
  catch(exception: UnLoginException, host: ArgumentsHost) {
    const response = host.switchToHttp().getResponse<Response>();

    response
      .status(HttpStatus.UNAUTHORIZED)
      .json({
        code: HttpStatus.UNAUTHORIZED,
        message: 'fail',
        data: exception.message || '用户未登录',
      })
      .end();
  }
}

在这个过滤器中,我们使用 @Catch 装饰器来指定它应该捕获 UnLoginException 异常。

当捕获到这个异常时,过滤器会返回一个包含未授权状态码(401)和自定义消息的 JSON 响应。

在控制器中,我们可以抛出 UnLoginException 测试:

访问页面:

使用异常过滤器

异常过滤器可以通过多种方式来使用:

  • 全局范围:可以在应用程序的全局范围内应用异常过滤器。
typescript 复制代码
// 绑定到全局
import { Module } from '@nestjs/common';

@Module({
  // ...
  providers: [
    {
      provide: APP_FILTER,
      useClass: HttpErrorFilter,
    },
  ],
})
export class AppModule {}
  • 控制器范围
typescript 复制代码
@Controller('cats')
@UseFilters(new HttpExceptionFilter())
export class CatsController {
  // ...
}
  • 方法范围
typescript 复制代码
@Controller('cats')
export class CatsController {
  @Post()
  @UseFilters(new HttpExceptionFilter())
  async create(@Body() createCatDto: CreateCatDto) {
    // ...
  }
}
相关推荐
Vicky&James几秒前
WPF到Web的无缝过渡:英雄联盟客户端项目OpenSilver迁移实战
前端·wpf
吃海鲜的骆驼2 分钟前
服务异步通讯与RabbitMQ
java·分布式·后端·rabbitmq
m0_748233644 分钟前
RabbitMQ 进阶
android·前端·后端
不想有bug的小菜鸟13 分钟前
vue3使用iframe全屏展示pdf效果
前端·pdf
m0_7482386313 分钟前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
u01005596014 分钟前
前端代理,解决跨域问题讲解
前端
quitv19 分钟前
react脚手架配置别名
前端·javascript·react.js
m0_5287238128 分钟前
前端如何进行性能优化
前端·性能优化
化作繁星29 分钟前
在 Vue 3 中,如何缓存和复用动态组件
前端·vue.js·缓存
一粒沙-1 小时前
iOS 将GIF图分享至微信
前端·ios