例外處理
@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
方法支援下列引數
方法引數 | 描述 |
---|---|
例外類型 |
用於存取引發的例外。 |
|
用於存取引發例外的控制器方法。 |
|
通用存取請求參數以及請求和 Session 屬性,而無需直接使用 Servlet API。 |
|
選擇任何特定的請求或回應類型(例如, |
|
強制 Session 的存在。因此,此類引數永遠不會是 |
|
目前已驗證的使用者 — 如果已知,則可能是特定的 |
|
請求的 HTTP 方法。 |
|
目前請求地區設定,由最特定的可用 |
|
與目前請求關聯的時區,由 |
|
用於存取 Servlet API 公開的原始回應 Body。 |
|
用於存取錯誤回應的模型。永遠為空。 |
|
指定在重新導向時要使用的屬性 — (即要附加到查詢字串)以及要暫時儲存直到重新導向後的請求的 Flash 屬性。請參閱 重新導向屬性 和 Flash 屬性。 |
|
用於存取任何 Session 屬性,與作為類別層級 |
|
用於存取請求屬性。有關更多詳細資訊,請參閱 |
傳回值
@ExceptionHandler
方法支援下列傳回值
傳回值 | 描述 |
---|---|
|
傳回值透過 |
|
傳回值指定要透過 |
|
若要呈現 RFC 9457 錯誤回應,並在 Body 中包含詳細資訊,請參閱 錯誤回應 |
|
若要呈現 RFC 9457 錯誤回應,並在 Body 中包含詳細資訊,請參閱 錯誤回應 |
|
要使用 |
|
要與隱含模型一起使用的 |
|
要新增至隱含模型的屬性,視圖名稱透過 |
|
要新增至模型的屬性,視圖名稱透過 請注意, |
|
要使用的視圖和模型屬性,以及可選的回應狀態。 |
|
如果方法同時具有 如果以上皆非,則對於 REST 控制器, |
任何其他傳回值 |
如果傳回值與以上任何一項都不匹配,並且不是簡單類型(由 BeanUtils#isSimpleProperty 決定),則預設情況下,它會被視為要新增至模型的模型屬性。如果是簡單類型,則保持未解析狀態。 |