異常
@Controller
和 @ControllerAdvice 類可以有 @ExceptionHandler
方法來處理控制器方法中的異常,如下例所示
-
Java
-
Kotlin
import java.io.IOException;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
@Controller
public class SimpleController {
@ExceptionHandler(IOException.class)
public ResponseEntity<String> handle() {
return ResponseEntity.internalServerError().body("Could not read file storage");
}
}
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.ExceptionHandler
import java.io.IOException
@Controller
class SimpleController {
@ExceptionHandler(IOException::class)
fun handle() : ResponseEntity<String> {
return ResponseEntity.internalServerError().body("Could not read file storage")
}
}
異常對映
異常可以匹配正在傳播的頂級異常(例如,直接丟擲的 IOException
)或包裝器異常中的巢狀原因(例如,包裝在 IllegalStateException
內部的 IOException
)。從 5.3 版本開始,這可以在任意原因級別進行匹配,而之前只考慮直接原因。
對於匹配異常型別,最好將目標異常宣告為方法引數,如前面的示例所示。當多個異常處理方法匹配時,通常優先選擇根異常匹配而不是原因異常匹配。更具體地說,ExceptionDepthComparator
用於根據異常型別到丟擲異常的深度對異常進行排序。
另外,註解宣告可以縮小要匹配的異常類型範圍,如下例所示
-
Java
-
Kotlin
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handleIoException(IOException ex) {
return ResponseEntity.internalServerError().body(ex.getMessage());
}
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handleIoException(ex: IOException): ResponseEntity<String> {
return ResponseEntity.internalServerError().body(ex.message)
}
你甚至可以在非常通用的引數簽名中使用特定異常型別的列表,如下例所示
-
Java
-
Kotlin
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handleExceptions(Exception ex) {
return ResponseEntity.internalServerError().body(ex.getMessage());
}
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handleExceptions(ex: Exception): ResponseEntity<String> {
return ResponseEntity.internalServerError().body(ex.message)
}
根異常和原因異常匹配的區別可能會令人驚訝。 在前面所示的 在 |
我們通常建議您在引數簽名中儘可能具體,以減少根異常和原因異常型別之間不匹配的可能性。考慮將一個多匹配方法分解為單獨的 @ExceptionHandler
方法,每個方法透過其簽名匹配一個特定的異常型別。
在多 @ControllerAdvice
的安排中,我們建議將您的主要根異常對映宣告在具有相應排序優先順序的 @ControllerAdvice
上。雖然根異常匹配優於原因異常匹配,但這僅在給定控制器或 @ControllerAdvice
類的方法之間定義。這意味著高優先順序 @ControllerAdvice
bean 上的原因匹配優先於低優先順序 @ControllerAdvice
bean 上的任何匹配(例如,根匹配)。
最後但同樣重要的是,@ExceptionHandler
方法實現可以選擇透過重新丟擲原始形式的給定異常例項來放棄處理它。這在您只對根級別匹配或在無法靜態確定的特定上下文中的匹配感興趣的場景中很有用。重新丟擲的異常會在剩餘的解析鏈中傳播,就像最初沒有匹配到該 @ExceptionHandler
方法一樣。
Spring MVC 中對 @ExceptionHandler
方法的支援是建立在 DispatcherServlet
級別的 HandlerExceptionResolver 機制之上的。
媒體型別對映
除了異常型別,@ExceptionHandler
方法還可以宣告可生產的媒體型別。這允許根據 HTTP 客戶端請求的媒體型別(通常在“Accept”HTTP 請求頭中)來最佳化錯誤響應。
應用可以直接在註解上宣告可生產的媒體型別,對於相同的異常型別
-
Java
-
Kotlin
@ExceptionHandler(produces = "application/json")
public ResponseEntity<ErrorMessage> handleJson(IllegalArgumentException exc) {
return ResponseEntity.badRequest().body(new ErrorMessage(exc.getMessage(), 42));
}
@ExceptionHandler(produces = "text/html")
public String handle(IllegalArgumentException exc, Model model) {
model.addAttribute("error", new ErrorMessage(exc.getMessage(), 42));
return "errorView";
}
@ExceptionHandler(produces = ["application/json"])
fun handleJson(exc: IllegalArgumentException): ResponseEntity<ErrorMessage> {
return ResponseEntity.badRequest().body(ErrorMessage(exc.message, 42))
}
@ExceptionHandler(produces = ["text/html"])
fun handle(exc: IllegalArgumentException, model: Model): String {
model.addAttribute("error", ErrorMessage(exc.message, 42))
return "errorView"
}
這裡,方法處理相同的異常型別,但不會被拒絕為重複。相反,請求“application/json”的 API 客戶端將收到 JSON 錯誤,而瀏覽器將獲得 HTML 錯誤檢視。每個 @ExceptionHandler
註解可以宣告多個可生產媒體型別,錯誤處理階段的內容協商將決定使用哪種內容型別。
方法引數
@ExceptionHandler
方法支援以下引數
方法引數 | 描述 |
---|---|
異常型別 |
用於訪問丟擲的異常。 |
|
用於訪問丟擲異常的控制器方法。 |
|
在不直接使用 Servlet API 的情況下通用地訪問請求引數以及請求和會話屬性。 |
|
選擇任何特定的請求或響應型別(例如, |
|
強制會話存在。因此,這樣的引數永遠不會是 |
|
當前已認證的使用者——如果已知,可能是特定的 |
|
請求的 HTTP 方法。 |
|
當前請求的 Locale,由最具體的 |
|
與當前請求關聯的時區,由 |
|
用於訪問 Servlet API 暴露的原始響應體。 |
|
用於訪問錯誤響應的模型。始終為空。 |
|
指定在重定向時使用的屬性——(即附加到查詢字串的屬性)以及臨時儲存到重定向後的請求的 Flash 屬性。詳見Redirect Attributes 和 Flash Attributes。 |
|
用於訪問任何會話屬性,與由於類級別的 |
|
用於訪問請求屬性。詳見 |
返回值
@ExceptionHandler
方法支援以下返回值
返回值 | 描述 |
---|---|
|
返回值透過 |
|
返回值指定將整個響應(包括 HTTP 頭和正文)透過 |
|
要渲染帶有正文詳細資訊的 RFC 9457 錯誤響應,詳見 Error Responses |
|
要渲染帶有正文詳細資訊的 RFC 9457 錯誤響應,詳見 Error Responses |
|
檢視名稱,將透過 |
|
一個 |
|
要新增到隱式模型中的屬性,檢視名稱透過 |
|
要新增到模型中的屬性,檢視名稱透過 請注意, |
|
要使用的檢視和模型屬性,以及可選的響應狀態。 |
|
如果一個方法的返回型別為 如果以上情況均不成立,對於 REST 控制器, |
任何其他返回值 |
如果返回值與上述任何型別都不匹配,且不是簡單型別(由 BeanUtils#isSimpleProperty 確定),預設情況下,它會被視為要新增到模型的模型屬性。如果它是簡單型別,則保持未解析狀態。 |