自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Springboot3新特性異常信息ProblemDetail詳解

開(kāi)發(fā) 前端
要為Spring WebFlux異常和任何ErrorResponseException啟用RFC 7807響應(yīng),需要擴(kuò)展 ResponseEntityExceptionHandler,并在Spring配置中把它聲明為@ControllerAdvice。處理程序有一個(gè)@ExceptionHandler方法,可以處理所有ErrorResponse異常,其中包括所有內(nèi)置的web異常。您可以添加更多的異常

環(huán)境:Springboot3.0.5

概述

RFC 7807定義了為HTTP響應(yīng)中錯(cuò)誤的可讀詳細(xì)信息,以避免需要為HTTP API定義新的錯(cuò)誤響應(yīng)格式。HTTP [RFC7230]狀態(tài)碼有時(shí)不足以傳達(dá)關(guān)于錯(cuò)誤的足夠信息。

RFC 7807 定義了簡(jiǎn)單的JSON[RFC7159]和XML[W3C.REC-XML-20081126]文檔格式以滿足此目的。它們被設(shè)計(jì)為可由HTTP API重用,HTTP API可以識(shí)別特定于其需求的不同“問(wèn)題類型”。

因此,API客戶端既可以知道高級(jí)錯(cuò)誤類(使用狀態(tài)碼),也可以知道問(wèn)題的細(xì)粒度細(xì)節(jié)。

例如,考慮一個(gè)響應(yīng),該響應(yīng)表明客戶的賬戶沒(méi)有足夠的權(quán)限。403禁止?fàn)顟B(tài)代碼可能被認(rèn)為是最適合使用的,因?yàn)樗鼘⑾騂TTP通用軟件(如客戶端庫(kù)、緩存和代理)通知響應(yīng)的一般語(yǔ)義。然而,這并沒(méi)有為API客戶端提供足夠的信息,說(shuō)明為什么禁止請(qǐng)求、適用的帳戶余額或如何糾正問(wèn)題。如果這些細(xì)節(jié)以可讀的格式包含在響應(yīng)體中,則客戶端可以適當(dāng)?shù)靥幚硭?;例如觸發(fā)將更多的信用轉(zhuǎn)移到賬戶中。

RFC 7807規(guī)范通過(guò)使用URI[RFC3986]識(shí)別特定類型的問(wèn)題(例如,“信用不足”)來(lái)實(shí)現(xiàn)這一點(diǎn);HTTP API可以通過(guò)指定受其控制的新URI或重用現(xiàn)有URI來(lái)實(shí)現(xiàn)這一點(diǎn)。

此外,Problem Detail信息可以包含其他信息,例如標(biāo)識(shí)問(wèn)題具體發(fā)生的URI(有效地為“Joe上周四沒(méi)有足夠的信用”這一概念提供了標(biāo)識(shí)符),這對(duì)于支持或取證目的可能很有用。

Problem Detail的數(shù)據(jù)模型是一個(gè)JSON[RFC7159]對(duì)象;當(dāng)格式化為JSON文檔時(shí),它使用“application/problem+json”媒體類型。

請(qǐng)注意,Problem Detail 不是在HTTP中傳達(dá)問(wèn)題細(xì)節(jié)的唯一方式;例如,如果響應(yīng)仍然是資源的表示,那么通常最好以該應(yīng)用程序的格式來(lái)描述相關(guān)細(xì)節(jié)。同樣,在許多情況下,有一個(gè)適當(dāng)?shù)腍TTP狀態(tài)代碼,不需要傳遞額外的細(xì)節(jié)。

Problem Detail消息格式

Problem Detail的規(guī)范模型是JSON對(duì)象。當(dāng)序列化為JSON文檔時(shí),該格式用“application/problem+json”媒體類型標(biāo)識(shí)。

例如,一個(gè)帶有JSONProblem Detail的HTTP響應(yīng):

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en
{
    "type": "https://pack.com/probs/out-of-credit",
    "title": "你沒(méi)有足夠的信用。",
    "detail": "你現(xiàn)在的余額是30,但是要花50。",
    "instance": "/account/12345/msgs/abc",
    "balance": 30,
    "accounts": ["/account/12345", "/account/67890"]
}

這里,結(jié)余不足問(wèn)題(由其類型URI標(biāo)識(shí))

  • type:標(biāo)識(shí)問(wèn)題類型的URI引用
  • title:中指明了403的原因
  • instance:給出了具體問(wèn)題發(fā)生的參考
  • detail:中給出了發(fā)生的具體細(xì)節(jié),并添加了兩個(gè)擴(kuò)展
  • balance:表示帳戶的余額
  • accounts:提供了可以充值帳戶的鏈接

傳遞問(wèn)題特定擴(kuò)展的能力允許傳遞多個(gè)問(wèn)題。例如:

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開(kāi)始支持Problem Detail。

REST服務(wù)的一個(gè)常見(jiàn)需求是在錯(cuò)誤響應(yīng)的主體中包含詳細(xì)信息。Spring框架支持“Problem Details for HTTP APIs”規(guī)范,即RFC 7807。

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

  • ProblemDetail — ?RFC 7807問(wèn)題細(xì)節(jié)的表示;一個(gè)簡(jiǎn)單的容器,用于規(guī)范中定義的標(biāo)準(zhǔn)字段和非標(biāo)準(zhǔn)字段。
  • ErrorResponse — 以RFC 7807的格式暴露HTTP錯(cuò)誤響應(yīng)細(xì)節(jié),包括HTTP狀態(tài)、響應(yīng)頭和響應(yīng)體;這允許異常封裝并暴露它們?nèi)绾斡成涞紿TTP響應(yīng)的細(xì)節(jié)。所有Spring MVC異常都實(shí)現(xiàn)了這一點(diǎn)。
  • ErrorResponseException?—?基本的ErrorResponse實(shí)現(xiàn),其他人可以作為一個(gè)方便的基類使用。
  • ResponseEntityExceptionHandler?—?@ControllerAdvice的方便基類,它處理所有Spring MVC異常,以及任何ErrorResponseException,并渲染一個(gè)帶有主體的錯(cuò)誤響應(yīng)。

Spring中要使用ProblemDetail首先需要通過(guò)如下配置開(kāi)啟:

spring:
  mvc:
    problemdetails:
      enabled: true

我們可以在任何使用@ExceptionHandler或任何@RequestMapping方法返回ProblemDetailErrorResponse以呈現(xiàn)RFC 7807響應(yīng)。處理方式如下:

  • ProblemDetail的status屬性決定了HTTP的狀態(tài)。
  • 如果還沒(méi)有設(shè)置,則從當(dāng)前URL路徑設(shè)置ProblemDetail的實(shí)例屬性。
  • 對(duì)于內(nèi)容協(xié)商,Jackson HttpMessageConverter在渲染ProblemDetail時(shí)更喜歡“application/problem+json”而不是“application/json”,如果沒(méi)有找到兼容的媒體類型,也會(huì)使用它。

要為Spring WebFlux異常和任何ErrorResponseException啟用RFC 7807響應(yīng),需要擴(kuò)展 ResponseEntityExceptionHandler,并在Spring配置中把它聲明為@ControllerAdvice。處理程序有一個(gè)@ExceptionHandler方法,可以處理所有ErrorResponse異常,其中包括所有內(nèi)置的web異常。您可以添加更多的異常處理方法,并使用protected方法將任何異常映射到ProblemDetail。

非標(biāo)準(zhǔn)字段

可以通過(guò)以下兩種方式之一使用非標(biāo)準(zhǔn)字段擴(kuò)展RFC7807響應(yīng)。

一、ProblemDetail類中有個(gè)Map集合的'properties'屬性。在使用Jackson庫(kù)時(shí),Spring框架注冊(cè)了ProblemDetailJacksonMixin,以確保這個(gè)“properties”映射被展開(kāi),并在響應(yīng)中作為頂級(jí)JSON屬性呈現(xiàn),同樣,反序列化期間的任何未知屬性都會(huì)插入到這個(gè)Map中。

你還可以擴(kuò)展ProblemDetail以添加專用的非標(biāo)準(zhǔn)屬性。ProblemDetail中的復(fù)制構(gòu)造函數(shù)允許從現(xiàn)有的ProblemDetail中輕松創(chuàng)建子類。這可以集中完成,例如從@ControllerAdvice,如ResponseEntityExceptionHandler,它將異常的ProblemDetail重新創(chuàng)建到一個(gè)具有額外非標(biāo)準(zhǔn)字段的子類中。

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;
}

測(cè)試接口:

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


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

示例1:

基礎(chǔ)使用

@RestControllerAdvice
public class GlobalExceptionHandler {
  
  // 當(dāng)發(fā)生異常后直接返回ProblemDetail對(duì)象
  @ExceptionHandler({Exception.class})
  public ProblemDetail handle(Exception e) {
    ProblemDetail detail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(500), e.getMessage());
    return detail ;
  }
}

運(yùn)行結(jié)果:

圖片

示例2:

添加擴(kuò)展屬性

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

運(yùn)行結(jié)果:

圖片

示例3:

繼承自ResponseEntityExceptionHandler該類中定義了@ExceptionHandler注解的方法,能夠處理大多數(shù)常見(jiàn)的異常。

@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提供的默認(rèn)實(shí)現(xiàn),要使用該類是需要通過(guò)如下配置開(kāi)啟:

spring.mvc.problemdetails.enabled=true

處理原理

當(dāng)返回結(jié)果是ProblemDetail或者ErrorResponse時(shí)通過(guò)如下類進(jìn)行解析處理:

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中的實(shí)現(xiàn)原理。

責(zé)任編輯:武曉燕 來(lái)源: 實(shí)戰(zhàn)案例錦集
相關(guān)推薦

2023-08-07 14:28:07

SpringBoot工具

2023-08-09 08:29:51

SpringWeb編程

2010-01-05 09:26:13

.NET 4.0

2010-10-12 09:52:02

ASP.NET MVC

2023-02-01 10:40:01

2013-07-29 15:13:35

2025-04-16 10:03:40

開(kāi)發(fā)Spring應(yīng)用程序

2023-08-11 08:59:49

分庫(kù)分表數(shù)據(jù)數(shù)據(jù)庫(kù)

2024-10-08 08:26:43

2022-10-26 07:14:25

Spring 6Spring業(yè)務(wù)

2010-10-08 14:32:32

ASP.NET MVCNuPack

2024-07-31 14:03:00

Spring定時(shí)任務(wù)管理

2009-06-29 17:42:03

Tapestry5新特

2009-07-27 09:46:28

Silverlight

2012-01-09 16:00:56

2023-08-08 08:23:08

Spring日志?線程池

2024-09-11 09:15:06

2009-02-04 17:33:24

ibmdwPython

2024-04-18 08:04:47

ElectronChrome升級(jí)

2024-03-04 08:19:11

SpringURLHeader
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)