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) {
    // ...
  }
}
相关推荐
J不A秃V头A23 分钟前
Vue3:编写一个插件(进阶)
前端·vue.js
无名指的等待71232 分钟前
SpringBoot中使用ElasticSearch
java·spring boot·后端
司篂篂1 小时前
axios二次封装
前端·javascript·vue.js
.生产的驴1 小时前
SpringBoot 消息队列RabbitMQ 消费者确认机制 失败重试机制
java·spring boot·分布式·后端·rabbitmq·java-rabbitmq
姚*鸿的博客1 小时前
pinia在vue3中的使用
前端·javascript·vue.js
宇文仲竹2 小时前
edge 插件 iframe 读取
前端·edge
AskHarries2 小时前
Spring Boot利用dag加速Spring beans初始化
java·spring boot·后端
Kika写代码2 小时前
【基于轻量型架构的WEB开发】【章节作业】
前端·oracle·架构
苹果酱05672 小时前
一文读懂SpringCLoud
java·开发语言·spring boot·后端·中间件
掐指一算乀缺钱2 小时前
SpringBoot 数据库表结构文档生成
java·数据库·spring boot·后端·spring