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) {
    // ...
  }
}
相关推荐
m0_748247551 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
刘大辉在路上1 小时前
突发!!!GitLab停止为中国大陆、港澳地区提供服务,60天内需迁移账号否则将被删除
git·后端·gitlab·版本管理·源代码管理
m0_748255022 小时前
前端常用算法集合
前端·算法
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203982 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2343 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1233 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
追逐时光者3 小时前
免费、简单、直观的数据库设计工具和 SQL 生成器
后端·mysql
初晴~4 小时前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·