Springboot3新特性异常信息ProblemDetail详解

news2024/11/17 3:39:57

环境:Springboot3.0.5

  • 概述

RFC 7807定义了为HTTP响应中错误的可读详细信息,以避免需要为HTTP API定义新的错误响应格式。HTTP [RFC7230]状态码有时不足以传达关于错误的足够信息。

RFC 7807 定义了简单的JSON[RFC7159]和XML[W3C.REC-XML-20081126]文档格式以满足此目的。它们被设计为可由HTTP API重用,HTTP API可以识别特定于其需求的不同“问题类型”。

因此,API客户端既可以知道高级错误类(使用状态码),也可以知道问题的细粒度细节。

例如,考虑一个响应,该响应表明客户的账户没有足够的权限。403禁止状态代码可能被认为是最适合使用的,因为它将向HTTP通用软件(如客户端库、缓存和代理)通知响应的一般语义。然而,这并没有为API客户端提供足够的信息,说明为什么禁止请求、适用的帐户余额或如何纠正问题。如果这些细节以可读的格式包含在响应体中,则客户端可以适当地处理它;例如触发将更多的信用转移到账户中。

RFC 7807规范通过使用URI[RFC3986]识别特定类型的问题(例如,“信用不足”)来实现这一点;HTTP API可以通过指定受其控制的新URI或重用现有URI来实现这一点。

此外,Problem Detail信息可以包含其他信息,例如标识问题具体发生的URI(有效地为“Joe上周四没有足够的信用”这一概念提供了标识符),这对于支持或取证目的可能很有用。

Problem Detail的数据模型是一个JSON[RFC7159]对象;当格式化为JSON文档时,它使用“application/problem+json”媒体类型。

请注意,Problem Detail 不是在HTTP中传达问题细节的唯一方式;例如,如果响应仍然是资源的表示,那么通常最好以该应用程序的格式来描述相关细节。同样,在许多情况下,有一个适当的HTTP状态代码,不需要传递额外的细节。

  • Problem Detail消息格式

Problem Detail的规范模型是JSON对象。当序列化为JSON文档时,该格式用“application/problem+json”媒体类型标识。

例如,一个带有JSONProblem Detail的HTTP响应:

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en
{
    "type": "https://pack.com/probs/out-of-credit",
    "title": "你没有足够的信用。",
    "detail": "你现在的余额是30,但是要花50。",
    "instance": "/account/12345/msgs/abc",
    "balance": 30,
    "accounts": ["/account/12345", "/account/67890"]
}

这里,结余不足问题(由其类型URI标识)

  • type:标识问题类型的URI引用

  • title:中指明了403的原因

  • instance:给出了具体问题发生的参考

  • detail:中给出了发生的具体细节,并添加了两个扩展

  • balance:表示帐户的余额

  • accounts:提供了可以充值帐户的链接

传递问题特定扩展的能力允许传递多个问题。例如:

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Content-Language: en
 {
   "type": "https://example.net/validation-error",
   "title": "Your request parameters didn't validate.",
   "invalid-params": [ {
      "name": "age",
      "reason": "must be a positive integer"
    },
    {
      "name": "color",
      "reason": "must be 'green', 'red' or 'blue'"
    }
  ]
}
  • Spring支持

从Spring6.x开始支持Problem Detail。

REST服务的一个常见需求是在错误响应的主体中包含详细信息。Spring框架支持“Problem Details for HTTP APIs”规范,即RFC 7807。

以下是此支持的主要抽象:

  • ProblemDetail —  RFC 7807问题细节的表示;一个简单的容器,用于规范中定义的标准字段和非标准字段。

  • ErrorResponse — 以RFC 7807的格式暴露HTTP错误响应细节,包括HTTP状态、响应头和响应体;这允许异常封装并暴露它们如何映射到HTTP响应的细节。所有Spring MVC异常都实现了这一点。

  • ErrorResponseException — 基本的ErrorResponse实现,其他人可以作为一个方便的基类使用。

  • ResponseEntityExceptionHandler — @ControllerAdvice的方便基类,它处理所有Spring MVC异常,以及任何ErrorResponseException,并渲染一个带有主体的错误响应。

Spring中要使用ProblemDetail首先需要通过如下配置开启:

spring:
  mvc:
    problemdetails:
      enabled: true

我们可以在任何使用@ExceptionHandler或任何@RequestMapping方法返回ProblemDetailErrorResponse以呈现RFC 7807响应。处理方式如下:

  • ProblemDetail的status属性决定了HTTP的状态。

  • 如果还没有设置,则从当前URL路径设置ProblemDetail的实例属性。

  • 对于内容协商,Jackson HttpMessageConverter在渲染ProblemDetail时更喜欢“application/problem+json”而不是“application/json”,如果没有找到兼容的媒体类型,也会使用它。

要为Spring WebFlux异常和任何ErrorResponseException启用RFC 7807响应,需要扩展 ResponseEntityExceptionHandler,并在Spring配置中把它声明为@ControllerAdvice。处理程序有一个@ExceptionHandler方法,可以处理所有ErrorResponse异常,其中包括所有内置的web异常。您可以添加更多的异常处理方法,并使用protected方法将任何异常映射到ProblemDetail。

非标准字段

可以通过以下两种方式之一使用非标准字段扩展RFC7807响应。

一、ProblemDetail类中有个Map集合的'properties'属性。在使用Jackson库时,Spring框架注册了ProblemDetailJacksonMixin,以确保这个“properties”映射被展开,并在响应中作为顶级JSON属性呈现,同样,反序列化期间的任何未知属性都会插入到这个Map中。

你还可以扩展ProblemDetail以添加专用的非标准属性。ProblemDetail中的复制构造函数允许从现有的ProblemDetail中轻松创建子类。这可以集中完成,例如从@ControllerAdvice,如ResponseEntityExceptionHandler,它将异常的ProblemDetail重新创建到一个具有额外非标准字段的子类中。

ProblemDetail类

public class ProblemDetail {
  private static final URI BLANK_TYPE = URI.create("about:blank");
  private URI type = BLANK_TYPE;
  @Nullable
  private String title;
  private int status;
  @Nullable
  private String detail;
  @Nullable
  private URI instance;
  @Nullable
  private Map<String, Object> properties;
}

测试接口:

@RestController
@RequestMapping("/demo")
public class DemoController {

  @GetMapping("")
  public Object index(Integer age) {
    System.out.println(1 / 0) ;
    return "success" ;
  }
  
}

示例1:

基础使用

@RestControllerAdvice
public class GlobalExceptionHandler {
  
  // 当发生异常后直接返回ProblemDetail对象
  @ExceptionHandler({Exception.class})
  public ProblemDetail handle(Exception e) {
    ProblemDetail detail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(500), e.getMessage());
    return detail ;
  }
}

运行结果:

示例2:

添加扩展属性

@RestControllerAdvice
public class GlobalExceptionHandler {
  
  // 这里使用的是ErrorResponse
  @ExceptionHandler({ArithmeticException.class})
  public ErrorResponse handle(Exception e) {
    ErrorResponse errorResponse = new ErrorResponseException(HttpStatusCode.valueOf(500), e) ;
    errorResponse.getBody().setProperty("operator_time", new Date()) ;
    return errorResponse ;
  }
  
}
运行结果:

示例3:

继承自ResponseEntityExceptionHandler该类中定义了@ExceptionHandler注解的方法,能够处理大多数常见的异常。

@ControllerAdvice
final class ProblemDetailsExceptionHandler extends ResponseEntityExceptionHandler {
}

ResponseEntityExceptionHandler

public abstract class ResponseEntityExceptionHandler implements MessageSourceAware {
  @ExceptionHandler({
    HttpRequestMethodNotSupportedException.class,
    HttpMediaTypeNotSupportedException.class,
    HttpMediaTypeNotAcceptableException.class,
    MissingPathVariableException.class,
    MissingServletRequestParameterException.class,
    MissingServletRequestPartException.class,
    ServletRequestBindingException.class,
    MethodArgumentNotValidException.class,
    NoHandlerFoundException.class,
    AsyncRequestTimeoutException.class,
    ErrorResponseException.class,
    ConversionNotSupportedException.class,
    TypeMismatchException.class,
    HttpMessageNotReadableException.class,
    HttpMessageNotWritableException.class,
    BindException.class
  })
  @Nullable
  public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {
    if (ex instanceof HttpMessageNotWritableException theEx) {
      return handleHttpMessageNotWritable(theEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);
    }
    // ...
  }
  protected ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
    ProblemDetail body = createProblemDetail(ex, status, "Failed to write request", null, null, request);
    return handleExceptionInternal(ex, body, headers, status, request);
  }
}

该类是Spring提供的默认实现,要使用该类是需要通过如下配置开启:

spring.mvc.problemdetails.enabled=true
  • 处理原理

当返回结果是ProblemDetail或者ErrorResponse时通过如下类进行解析处理:

public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodProcessor {
  public boolean supportsReturnType(MethodParameter returnType) {
    Class<?> type = returnType.getParameterType();
    return ((HttpEntity.class.isAssignableFrom(type) && !RequestEntity.class.isAssignableFrom(type)) || ErrorResponse.class.isAssignableFrom(type) || ProblemDetail.class.isAssignableFrom(type));
  }
  public void handleReturnValue(...) throws Exception {
    HttpEntity<?> httpEntity;
    if (returnValue instanceof ErrorResponse response) {
      httpEntity = new ResponseEntity<>(response.getBody(), response.getHeaders(), response.getStatusCode());
    } else if (returnValue instanceof ProblemDetail detail) {
      httpEntity = ResponseEntity.of(detail).build();
    }
    if (httpEntity.getBody() instanceof ProblemDetail detail) {
      if (detail.getInstance() == null) {
        URI path = URI.create(inputMessage.getServletRequest().getRequestURI());
        detail.setInstance(path);
      }
    }
    // ...
    writeWithMessageConverters(httpEntity.getBody(), returnType, inputMessage, outputMessage);
    outputMessage.flush();
  }
}

以上就是ProblemDetail在Spring中的实现原理。

完毕!!!

三连

 图片

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/710969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

7.MHA高可用配置及故障切换

文章目录 MHA高可用配置及故障切换MHA概念实验配置时间同步与主从复制安装MHA服务SSH免交互认证验证MHA服务是否开启启动服务 故障模拟恢复故障过程 MHA高可用配置及故障切换 MHA概念 MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切…

计网实验第四章:IP

问题集1&#xff1a; 1.192.168.31.7 2. 上层协议为ICMP&#xff0c;并且其字段值为1 3. 20字节 56字节 - 20字节 36字节 解释&#xff1a;如图所示 4. 这个报文段没有分段发送 原因如图&#xff1a;按照下图所示 标志位显示没有更多的分段。 5. 同一地址发送的这两个…

1.计算机是如何工作的(下)

文章目录 4.编程语言&#xff08;Program Language&#xff09;4.1程序&#xff08;Program&#xff09;4.2早期编程4.3编程语言发展 5.操作系统&#xff08;Operating System&#xff09;5.1操作系统的定位5.2什么是进程/任务&#xff08;Process/Task&#xff09;5.3进程控制…

[LangChain]简介快速入门

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;人工智能。 目录 1、简介2、快速入门2.1、LLMs2.2、聊天模型2.3、提示模板2.4、链2.5、代理2.6、内存 1、简介 …

1758_C语言通过预处理的eror输出异常信息

全部学习汇总&#xff1a; GreyZhang/c_basic: little bits of c. (github.com) 这个功能我一直没有使用过&#xff0c;能够想到它完全是因为之前从某些代码中看了一眼。或许&#xff0c;这就是我跟这个小小知识点的缘分。 最近想处理一个可以适配多种情况的程序&#xff0c;…

领域驱动设计(DDD,Domain-Driven Design)

领域驱动设计 前言正文领域驱动设计基本概念什么是领域模型&#xff1f;什么是领域服务&#xff08;Domain Service&#xff09;&#xff1f;什么是领域事件&#xff1f; 秒杀项目中的领域分析一、秒杀活动领域设计秒杀活动领域模型领域服务领域事件 二、秒杀品领域设计领域模型…

开源自动化测试框架介绍

开源自动化测试框架介绍 一、Junit&#xff08;白盒测试、API自动化、UI自动化&#xff09;【官网】【简介】【使用场景】 二、Selenium&#xff08;Web自动化、爬虫&#xff09;【官网】【简介】【使用场景】 三、TestNG&#xff08;白盒测试、API自动化、UI自动化&#xff09…

Linux文件系统架构和共享文件方法

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天来聊聊 Linux文件系统架构和共享文件方法。 在Linux环境中使用文件和目录是工作中不可回避的环节。当然&#xff0c;在我的博客里成立windows程序员看linux这个专题&#xff0c;主要还是因为微软也发布了…

LwIP系列(4):ARP协详解

前言 对于应用程序而言&#xff0c;我们与其他设备、服务通信&#xff0c;主要通过域名、IP进行通信&#xff0c;而以太网底层驱动&#xff0c;最终是通过MAC地址来表示设备的唯一标识&#xff0c;即IP是逻辑地址&#xff0c;并不是物理地址。在上一篇文章中&#xff0c;我们也…

【ArcGIS Pro二次开发】(45):手搓一个工具执行进度框

我在之前做的工具中&#xff0c;UI这部分基本没怎么深入&#xff0c;都是直接用的现成的控件。 其中有一个问题比较突出&#xff0c;就是没有工具执行的进度框提示。曾经也用过系统自带的信息提示框和进度条&#xff0c;但太简陋&#xff0c;确实不好用。于是就想抄一个进度框…

Linux 读文件 - readahead预读算法

顺序读场景 intmain{charc[ 4096];intin -1;in open( "news.txt", O_RDONLY);intindex 0;while(read(in, &c, 4096) 4096){printf( "index: %d,len: %ld.\n",index, strlen(c));memset(c, 0, sizeof(c));index;} 数据结构 /** Track a single fi…

CentOS7 静默方式安装 Oracle19C

CentOS7 静默方式安装 Oracle19C 操作系统&#xff1a;CentOS7 Oracle&#xff1a; 19C 安装常用工具和依赖 yum -y install vim tar net-tools wget perl python3 readline* deltarpm python-deltarpm \zip unzip bc compat-libcap1* compat-libcap* binutils compat-libstdc…

最近的感悟与总结

目录 一、判别填鸭、与被填鸭的思考讲解人&#xff1a;听者&#xff1a; 二、最近感悟&#xff1a;三、再来三道数学题吧&#xff1a;四、总结 一、判别填鸭、与被填鸭的思考 讲解人&#xff1a; 1.是否在讲解过程中增加了知识认识的维度&#xff1f;(具体是什么&#xff0c;…

08_Linux按键输入

目录 Linux下按键驱动原理 修改设备树文件 添加KEY设备节点 按键驱动程序编写 编写测试APP 运行测试 Linux下按键驱动原理 按键驱动和LED驱动原理上来讲基本都是一样的,都是操作GPIO,只不过一个是读取GPIO 的高低电平,一个是从GPIO输出高低电平。实现按键输入,在驱动程序…

nginx的安装与自启动配置

1. nginx源码下载 nginx源码下载 2. nginx编译安装 2.1 解压安装包 tar -zxvf nginx-1.24.0.tar.gz2.2 编译安装 cd nginx-1.24.0 ./configure make make install执行./configure时可能出现如下的error&#xff0c;需要安装依赖库&#xff1a; 缺少pcre库 下载pcre安装包…

链码的安装、实例化、查询、调用

目录 1、首先保证网络已经处于启动状态 2、进入CLI容器 3、检查当前节点已经加入到哪些通道中&#xff08;刚进来&#xff0c;默认是 peer0.example.com&#xff09; 链码的安装 1、检查通道名称是否设置正确 2、使用install安装链码 3、实例化链码 4、查詢链码 发起交易 …

English Learning - L3 作业打卡 Lesson7 Day52 2023.6.27 周二

English Learning - L3 作业打卡 Lesson7 Day52 2023.6.27 周二 引言&#x1f349;句1: And that is when it dawned on me that I didn’t have to be 5,5 ft. anymore.成分划分弱读连读爆破语调 &#x1f349;句2: I could be as tall as I wanted, or as short as I wanted …

【电子学会】2023年05月图形化三级 -- 躲避陨石

文章目录 躲避陨石1. 准备工作2. 功能实现3. 设计思路与实现&#xff08;1&#xff09;角色、舞台背景设置a. 角色设置b. 舞台背景设置 &#xff08;2&#xff09;脚本编写a. 角色&#xff1a;Rocketshipb. 角色&#xff1a;Ball 4. 评分标准5. 知识块 躲避陨石 飞船在陨石乱飞…

MySQL数据库第一课 ---------安装

作者前言 欢迎小可爱们前来借鉴我的gtiee秦老大大 (qin-laoda) - Gitee.com 目录 虚拟环境虚拟环境 安装虚拟机 自主安装 简易安装 ———————————————————————————————————————— 虚拟环境虚拟环境 我们现在运行代码的环境是在 PyCharm…

大型语言模型作为属性化训练数据生成器

大型语言模型作为属性化训练数据生成器&#xff0c;提出一种使用多样化属性提示的数据生成方法&#xff0c;可以生成具有多样性和属性的训练数据&#xff0c;从而提高了模型的性能和数据生成的效率。 动机&#xff1a;大型语言模型(LLM)最近被用作各种自然语言处理(NLP)任务的…