NestJS 的 异常过滤器学习

这篇具有很好参考价值的文章主要介绍了NestJS 的 异常过滤器学习。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

NestJS 带有一个内置的异常层,负责处理应用程序中所有未处理的异常。当应用程序代码未处理异常时,该层会捕获异常,然后自动发送对应的异常响应给用户。

在 NestJs 中默认异常过滤器为HttpException及其子类,当异常无法不属于HttpException或者子类的时候,内置的异常过滤器会生成如下的 JSON 响应:

{
  "statusCode": 500,
  "message": "Internal server error"
}

使用 HttpException 抛出异常

使用HttpException抛出异常是很简单的,我们主要创建HttpException异常就可以。

就比如我们有一个保存的请求地址,这个接口需要在请求头中得Accept参数带入 Token,假如没有传入此参数或者传入的是*/*的字符串的话,我们就抛出没有授权的异常。

@Post('/save')
saveCommodity(@Body() commodity: Commodity, @Headers('Accept') token: string) {
  if (token !== '*/*' || token) {
    this.commodityService.create(commodity)
  } else {
    throw new HttpException('无权限操作', HttpStatus.UNAUTHORIZED)
  }
}

我们没有携带Accept请求接口后,我们收到的响应数据如下:

{
  "statusCode": 401,
  "message": "无权限操作"
}

从上述的例子我们可以看到HttpException的构造函数必须接收两个参数:

  • 响应状态码
  • 返回的消息,消息可以是字符串、数字和对象
  • 提供错误的原因,它不会被记录到响应的数据中,但是我们可以用于错误日志的收集,这样有利于我们遇到问题的时候可以很好的对问题进行定位分析

具体实例如下:

@Post('/save')
saveCommodity(@Body() commodity: Commodity) {
  try {
    await this.commodityService.create(commodity)
  } catch (error) {
    throw new HttpException({
      status: HttpStatus.UNAUTHORIZED,
      error: '系统异常',
    }, HttpStatus.UNAUTHORIZED, {
      cause: error
    });
  }
}

上述的接口发生异常时,返回的结果如下:

{
  "status": 401,
  "error": "系统异常"
}

自定义异常

有些时候因业务的需求我们可能需要编写自定义异常,那么自定义异常必须继承HttpException类。具体的例子如下:

export class ForbiddenException extends HttpException {
  constructor(errorInfo: ForbiddenExceptionInterface) {
    // 可以自定义接受的参数,我们就可以对捕获到的异常进行处理
    console.log(errorInfo.message);
    super(errorInfo.type, errorInfo.start);
  }
}

内置 HTTP 异常

NestJs 提供的内置 Http 异常如下:

异常类 异常说明
BadRequestException 错误请求异常,比如请求的参数类型不对或者请求参数缺失的时候都可以返回这个错误
UnauthorizedException 当用户没有登录就调用需要鉴权的接口的话,就可以抛出这个问题
NotFoundException 类似于 404 错误的抛出
ForbiddenException 类似于 403 错误
NotAcceptableException 类似于 406 错误
RequestTimeoutException 请求超时异常
ConflictException 类似于 409 错误
GoneException 类似于 410 错误
HttpVersionNotSupportedException HTTP 的版本不对的时候可以抛出这个异常
PayloadTooLargeException 接口负载太高的时候可以抛出这个异常
UnsupportedMediaTypeException 当上传的文件类型不符合条件的时候就可以抛出这个异常
UnprocessableEntityException 比如我们添加某条数据时会提交对应的实体,假如请求的实体参数与定义的实体不匹配的时候就可以抛出这个异常
InternalServerErrorException 类似于 500 错误
NotImplementedException 当服务类调用了未实现的方法,我们可以抛出这个异常
ImATeapotException 类似于 226 错误
MethodNotAllowedException 比如请求接口是 Get 方式的,但是前端调用是 Post 的时候,我们可以抛出这个异常
BadGatewayException 网关出现错误的时候可以抛出这个异常
ServiceUnavailableException 当服务类不存在或者服务内部出现错误的时候就可以抛出这个异常
GatewayTimeoutException 网关超时的时候就可以抛出这个异常
PreconditionFailedException 类似于 412 错误

所有内置异常还可以 cause 使用以下参数提供错误和错误描述 options:

throw new BadRequestException("请求参数有误", {
  cause: new Error(),
  description: "姓名不能为数字",
});

上述的异常返回的数据如下:

{
  "message": "请求参数有误",
  "error": "姓名不能为数字",
  "statusCode": 400
}

异常过滤器

虽然基本(内置)异常过滤器可以自动为您处理许多情况,但您可能希望完全控制异常层。例如,您可能想要添加日志记录或根据某些动态因素使用不同的 JSON 架构。异常过滤器正是为此目的而设计的。它们使您可以控制确切的控制流程以及发送回客户端的响应内容。

我们创建一个异常过滤器类,负责捕获HttpException并且重组返回的异常数据。具体的代码如下:

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

@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,
    });
  }
}

上述代码捕获到异常后,会把返回的结构体转为如下的形式,并非默认的数据结构:

{
  "statusCode": 401,
  "timestamp": "2023-07-29T04:21:08.189Z",
  "path": "/commodity/save"
}

注意:所有异常过滤器都应该实现通用 ExceptionFilter接口。这要求您提供 catch(exception: T, host: ArgumentsHost)带有指定签名的方法。T 表示异常的类型。

装饰@Catch(HttpException)器将所需的元数据绑定到异常过滤器,告诉 Nest 这个特定的过滤器正在寻找类型的异常 HttpException,而不是其他任何东西。装饰@Catch()器可以采用单个参数或逗号分隔的列表。这使您可以同时为多种类型的异常设置过滤器。例如如下的代码:

@Catch(HttpException, BadRequestException)

参数主机

通过上述的例子我们可以看到catch()方法主要是接收两个参数,一个是exception用于获取当前处理的异常对象,另外一个是host它是ArgumentsHost对象,在后续的笔记中说明这个对象。

上述的例子中我们使用host来获取对传递给原始请求处理程序(在引发异常的控制器中)的和对象的引用。在此代码示例中,我们使用了一些辅助方法来获取所需的对象。

绑定过滤器

异常过滤器的使用很简单,使用@UseFulter()装饰器就可以完成过滤器的绑定,具体的例子如下:

@Post('/save')
@UseFilters(new HttpExceptionFilter())
saveCommodity(@Body() commodity: Commodity, @Headers('Accept') token: string) {
  if (token !== '*/*' && token) {
    this.commodityService.create(commodity)
  } else {
    throw new ForbiddenException({ message: '这是自定义错误', type: 'UNAUTHORIZED', start: HttpStatus.UNAUTHORIZED })
  }
}

@UserFilters()装饰器可以接收单个过滤器实例,也可以接收多个实例,多个实例之间可以用逗号分隔。或者我们可以直接传递过滤器类,让框架帮我们实例化过滤器并帮我们依赖注入。

@Post('/save')
@UseFilters(HttpExceptionFilter)
saveCommodity(@Body() commodity: Commodity, @Headers('Accept') token: string) {
  if (token !== '*/*' && token) {
    this.commodityService.create(commodity)
  } else {
    throw new ForbiddenException({ message: '这是自定义错误', type: 'UNAUTHORIZED', start: HttpStatus.UNAUTHORIZED })
  }
}

注意:尽可能使用类而不是实例来应用过滤器。它减少了内存使用量,因为 Nest 可以轻松地在整个模块中重用同一类的实例。

异常过滤器不仅仅可以在控制器上进行绑定还可以在全局访问进行绑定。全局访问的过滤器我们可以这样进行声明:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}

注意:useGlobalFilters()方法不会为网关和混合应用程序设置过滤器。

全局访问的过滤器是应用于整个应用程序、每个控制器和每个路由处理程序的。当我们需要在某个模块注入全局过滤器的依赖项是不行的,因为全局的过滤器是在没有上下文环境下完成,所以没有上下文的支持,模块是不能找到对应实例。为了解决这个问题,我们可以用下面的方式来进行过滤器的注入:

import { Module } from "@nestjs/common";
import { APP_FILTER } from "@nestjs/core";

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
  ],
})
export class AppModule {}

采用上述的方式进行异常过滤器的注入,注入后的过滤器还是全局的。

捕获所有类型的异常

为了捕获每个未处理的异常(无论异常类型如何),请将@Catch()装饰器的参数列表留空,例如@Catch()。例如如下的代码:文章来源地址https://www.toymoban.com/news/detail-620590.html

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from "@nestjs/common";
import { HttpAdapterHost } from "@nestjs/core";

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly httpAdapterHost: HttpAdapterHost) {}

  catch(exception: unknown, host: ArgumentsHost): void {
    // In certain situations `httpAdapter` might not be available in the
    // constructor method, thus we should resolve it here.
    const { httpAdapter } = this.httpAdapterHost;

    const ctx = host.switchToHttp();

    const httpStatus =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const responseBody = {
      statusCode: httpStatus,
      timestamp: new Date().toISOString(),
      path: httpAdapter.getRequestUrl(ctx.getRequest()),
    };

    httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
  }
}

到了这里,关于NestJS 的 异常过滤器学习的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JavaWEB学习笔记(二)------HTTP、Servlet、会话、过滤器、监听器、Ajax、前端工程化

    目录 HTTP HTTP1.1 请求和响应的报文格式 请求报文 响应报文 常见状态响应码 Servlet  静态资源和动态资源 ​编辑  Servlet简介  Servlet开发流程 导入和响应头问题 url-pattern不同写法 url-pattern工作方式 Servlet注解方式配置 Servlet生命周期  Servlet继承结构 Servlet接口 GenerisServlet类 Ht

    2024年01月21日
    浏览(38)
  • SpringCloudGateway学习(2)-过滤器

     Gateway 作用:过滤器就是在请求的传递过程中,对请求和响应做一些手脚。  Gateway 生命周期:Pre Post。 PRE :这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。 POST :这种过滤器在路由到微服务以后执

    2024年02月12日
    浏览(38)
  • FFMPEG 视频类过滤器学习整理

    针对FFMPEG提供视频过滤器进行了介绍,并提供使用实例 addroi 作用 在视频帧上标记一块感兴趣的区域。 帧数据被原封不动地传递,但元数据被附加到帧,指示可能影响后续编码行为的感兴趣区域。可以通过多次应用过滤器来标记多个区域。 参数 qoffset: 应用在此区域的量化偏

    2024年02月07日
    浏览(35)
  • 【Qt学习】06:事件与事件过滤器

    一、事件 事件event是由系统或者Qt本身在不同的时刻发出的,主要包括用户事件与定时器事件, 当用户按下鼠标、敲下键盘或者是窗口需要重新绘制的时候,都会发出一个相应的事件,一些事件是在对用户操作做出响应时进行发出(如键盘事件等);另一些事件则是由系统自

    2024年02月11日
    浏览(36)
  • 【C++学习】哈希的应用—位图与布隆过滤器

    文章简介 : 在这篇文章中,你会学习到关于哈希思想的最常见的两个应用,也就是 位图 与 布隆过滤器 , 文章会讲解位图和布隆过滤器的概念,底层实现,对应的适应的场景,以及相关经典 海量数据面试题 及解析。 所谓位图,就是用每一位来存放某种状态,适用于 海量

    2024年04月14日
    浏览(60)
  • 开发笔记 | JAVA过滤器Filter实现全局接口入参去除前后空格

    目录 思考过程 遇到的问题 过滤器Filter使用步骤 全局去除入参前后空格代码实现 处理过程中自己造成的一些问题 需求背景: 前端所有的条件查询去除前后空格,如搜 【\\\"   测试    \\\"】后端将其转为【测试】。之前都是前端统一处理的,但是这次要后端处理,那么就得考虑

    2024年02月12日
    浏览(39)
  • 【微服务笔记17】微服务组件之Gateway实现动态路由、配置路由规则、路由过滤器

    这篇文章,主要介绍微服务组件之Gateway实现动态路由、配置路由映射规则、路由过滤器。 目录 一、动态路由配置 1.1、动态URI路由配置 (1)引入eureka客户端依赖 (2)添加路由配置 1.2、服务名称转发配置 二、断言配置规则 2.1、路由断言工厂类 2.2、Path路径匹配 2.3、Query请求

    2023年04月23日
    浏览(44)
  • jQuery选择器(二)(基本过滤器,内容过滤器,可见过滤器)

    写在前面 jQuery是一个快速、简洁的 JavaScript 框架,是继Prototype之后又一个优秀的 JavaScript 代码库。jQuery的设计宗旨是“WriteLess,DoMore”,即倡导写更少的代码,做 更多的事情。jQuery封装了 JavaScript 常用的功能代码,提供一种简便的 JavaScript 设计模式,优化HTML文档操作、事件

    2024年02月02日
    浏览(52)
  • Elasticsearch基础篇(五):创建es索引并学习分析器、过滤器、分词器的作用和配置

    Elasticsearch 是一个分布式搜索和分析引擎,它使用JSON文档来存储数据。索引是Elasticsearch中数据的基本组织单元之一,下面是Elasticsearch索引相关的基本概念: 结构元素 Elasticsearch MySQL 数据库 索引(Index) 数据库(Database) 表格 类型(Type)* 表(Table) 记录/行 文档(Document)

    2024年02月03日
    浏览(85)
  • Gateway自定义过滤器——全局过滤器

    首先,我们要知道全局过滤器其实是特殊路由过滤器(特殊的GatewayFilter),会有条件地作用于所有路由。 为什么要自定义全局过滤器?就好比是看大门的保安大叔,平时主要是做好进出大门外来人员登记即可,但是因为新冠疫情,现在还需要给外来人员测量体温等等。而已有的

    2024年02月16日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包