当前位置:首页 >娱乐 >Springboot3新特性异常信息ProblemDetail详解 它们被设计为可由HTTP API重用

Springboot3新特性异常信息ProblemDetail详解 它们被设计为可由HTTP API重用

2024-07-01 07:37:14 [百科] 来源:避面尹邢网

Springboot3新特性异常信息ProblemDetail详解

作者:Springboot实战案例锦集 Spr 开发 前端 要为Spring WebFlux异常和任何ErrorResponseException启用RFC 7807响应,新特性异息P详解需要扩展 ResponseEntityExceptionHandler,常信并在Spring配置中把它声明为@ControllerAdvice。新特性异息P详解处理程序有一个@ExceptionHandler方法,常信可以处理所有ErrorResponse异常,新特性异息P详解其中包括所有内置的常信web异常。您可以添加更多的新特性异息P详解异常

环境:Springboot3.0.5

概述

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

Springboot3新特性异常信息ProblemDetail详解 它们被设计为可由HTTP API重用

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

Springboot3新特性异常信息ProblemDetail详解 它们被设计为可由HTTP API重用

因此,新特性异息P详解API客户端既可以知道高级错误类(使用状态码),常信也可以知道问题的新特性异息P详解细粒度细节。

Springboot3新特性异常信息ProblemDetail详解 它们被设计为可由HTTP 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 ForbiddenContent-Type: application/problem+jsonContent-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 RequestContent-Type: application/problem+jsonContent-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:

基础使用

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

运行结果:

图片

示例2:

添加扩展属性

@RestControllerAdvicepublic 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注解的方法,能够处理大多数常见的异常。

@ControllerAdvicefinal 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中的实现原理。

责任编辑:武晓燕 来源: 实战案例锦集 RFCwebSpring

(责任编辑:综合)

    推荐文章
    热点阅读