錯誤響應

對於 REST 服務來說,在錯誤響應體中包含詳細資訊是一個常見的需求。Spring Framework 支援“HTTP API 的問題詳情”規範,RFC 9457

以下是此支援的主要抽象

  • ProblemDetail — RFC 9457 問題詳情的表示;一個簡單的容器,包含規範中定義的標準欄位和非標準欄位。

  • ErrorResponse — 公開 HTTP 錯誤響應詳情的契約,包括 HTTP 狀態、響應頭和 RFC 9457 格式的響應體;這使得異常能夠封裝並公開它們如何對映到 HTTP 響應的詳情。所有 Spring MVC 異常都實現了此介面。

  • ErrorResponseException — 基本的 ErrorResponse 實現,其他類可以方便地將其用作基類。

  • ResponseEntityExceptionHandler — @ControllerAdvice 的便捷基類,用於處理所有 Spring MVC 異常以及任何 ErrorResponseException,並渲染帶響應體的錯誤響應。

渲染

您可以從任何 @ExceptionHandler@RequestMapping 方法返回 ProblemDetailErrorResponse 以渲染 RFC 9457 響應。處理過程如下

  • ProblemDetailstatus 屬性決定 HTTP 狀態。

  • ProblemDetailinstance 屬性會在未設定時從當前 URL 路徑設定。

  • 對於內容協商,在渲染 ProblemDetail 時,Jackson HttpMessageConverter 優先選擇“application/problem+json”而非“application/json”,如果找不到相容的媒體型別,也會回退到它。

要為 Spring MVC 異常和任何 ErrorResponseException 啟用 RFC 9457 響應,請擴充套件 ResponseEntityExceptionHandler 並將其宣告為 Spring 配置中的 @ControllerAdvice。該 handler 有一個 @ExceptionHandler 方法,用於處理任何 ErrorResponse 異常,包括所有內建的 Web 異常。您可以新增更多異常處理方法,並使用一個保護方法將任何異常對映到 ProblemDetail

您可以透過帶有 WebMvcConfigurerMVC 配置註冊 ErrorResponse 攔截器。使用它來攔截任何 RFC 9457 響應並執行一些操作。

非標準欄位

您可以透過以下兩種方式之一使用非標準欄位擴充套件 RFC 9457 響應。

其一,插入到 ProblemDetail 的“properties” Map 中。使用 Jackson 庫時,Spring Framework 註冊了 ProblemDetailJacksonMixin,它確保將此“properties” Map 解包並作為頂級 JSON 屬性渲染到響應中,同樣,反序列化期間任何未知屬性也會插入到此 Map 中。

您也可以擴充套件 ProblemDetail 以新增專用的非標準屬性。ProblemDetail 中的複製構造器允許子類輕鬆地從現有 ProblemDetail 建立新例項。這可以集中處理,例如,從像 ResponseEntityExceptionHandler 這樣的 @ControllerAdvice 中實現,它將異常的 ProblemDetail 重建為一個包含額外非標準欄位的子類例項。

定製和國際化 (i18n)

定製和國際化錯誤響應詳情是一個常見的需求。定製 Spring MVC 異常的問題詳情以避免暴露實現細節也是一個好的實踐。本節介紹對此的支援。

ErrorResponse 公開針對“type”、“title”和“detail”的訊息程式碼,以及針對“detail”欄位的訊息程式碼引數。ResponseEntityExceptionHandler 透過 MessageSource 解析這些程式碼,並相應地更新對應的 ProblemDetail 欄位。

訊息程式碼的預設策略如下

  • “type”:problemDetail.type.[異常類的完全限定名]

  • “title”:problemDetail.title.[異常類的完全限定名]

  • “detail”:problemDetail.[異常類的完全限定名][字尾]

ErrorResponse 可能公開多個訊息程式碼,通常在預設訊息程式碼後新增字尾。下表列出了 Spring MVC 異常的訊息程式碼和引數

異常 訊息程式碼 訊息程式碼引數

AsyncRequestTimeoutException

(預設)

ConversionNotSupportedException

(預設)

{0} 屬性名, {1} 屬性值

HandlerMethodValidationException

(預設)

{0} 列出所有驗證錯誤。每個錯誤的訊息程式碼和引數也透過 MessageSource 解析。

HttpMediaTypeNotAcceptableException

(預設)

{0} 支援的媒體型別列表

HttpMediaTypeNotAcceptableException

(預設)+ ".parseError"

HttpMediaTypeNotSupportedException

(預設)

{0} 不支援的媒體型別, {1} 支援的媒體型別列表

HttpMediaTypeNotSupportedException

(預設)+ ".parseError"

HttpMessageNotReadableException

(預設)

HttpMessageNotWritableException

(預設)

HttpRequestMethodNotSupportedException

(預設)

{0} 當前 HTTP 方法, {1} 支援的 HTTP 方法列表

MethodArgumentNotValidException

(預設)

{0} 全域性錯誤列表, {1} 欄位錯誤列表。每個錯誤的訊息程式碼和引數也透過 MessageSource 解析。

MissingRequestHeaderException

(預設)

{0} 頭名稱

MissingServletRequestParameterException

(預設)

{0} 請求引數名

MissingMatrixVariableException

(預設)

{0} 矩陣變數名

MissingPathVariableException

(預設)

{0} 路徑變數名

MissingRequestCookieException

(預設)

{0} cookie 名稱

MissingServletRequestPartException

(預設)

{0} 部分名稱

NoHandlerFoundException

(預設)

NoResourceFoundException

(預設)

TypeMismatchException

(預設)

{0} 屬性名, {1} 屬性值

UnsatisfiedServletRequestParameterException

(預設)

{0} 引數條件列表

與其他異常不同,MethodArgumentValidExceptionHandlerMethodValidationException 的訊息引數基於 MessageSourceResolvable 錯誤列表,這些錯誤也可以透過 MessageSource 資源包進行定製。有關更多詳情,請參閱定製驗證錯誤

客戶端處理

客戶端應用在使用 WebClient 時可以捕獲 WebClientResponseException,在使用 RestTemplate 時可以捕獲 RestClientResponseException,並使用其 getResponseBodyAs 方法將錯誤響應體解碼為任何目標型別,例如 ProblemDetailProblemDetail 的子類。