異常

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

根異常匹配和原因異常匹配之間的區別可能令人驚訝。

在前面所示的 IOException 變體中,該方法通常以實際的 FileSystemExceptionRemoteException 例項作為引數呼叫,因為它們都擴充套件自 IOException。但是,如果任何此類匹配異常在本身是 IOException 的包裝器異常中傳播,則傳入的異常例項是該包裝器異常。

handle(Exception) 變體中,行為甚至更簡單。在包裝場景中,它始終使用包裝器異常呼叫,在這種情況下,實際匹配的異常可以透過 ex.getCause() 找到。傳入的異常僅在這些異常作為頂級異常丟擲時才是實際的 FileSystemExceptionRemoteException 例項。

我們通常建議你在引數簽名中儘可能具體,以減少根異常型別和原因異常型別之間不匹配的可能性。考慮將多匹配方法拆分為單獨的 @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 方法支援以下引數

方法引數 描述

異常型別

用於訪問丟擲的異常。

HandlerMethod

用於訪問丟擲異常的控制器方法。

WebRequest, NativeWebRequest

無需直接使用 Servlet API 即可通用訪問請求引數以及請求和會話屬性。

jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse

選擇任何特定的請求或響應型別(例如,ServletRequestHttpServletRequest 或 Spring 的 MultipartRequestMultipartHttpServletRequest)。

jakarta.servlet.http.HttpSession

強制存在會話。因此,此類引數永遠不會為 null
請注意,會話訪問不是執行緒安全的。如果允許多個請求併發訪問會話,請考慮將 RequestMappingHandlerAdapter 例項的 synchronizeOnSession 標誌設定為 true

java.security.Principal

當前已認證的使用者——如果已知,可能是特定的 Principal 實現類。

HttpMethod

請求的 HTTP 方法。

java.util.Locale

當前請求區域設定,由最具體的 LocaleResolver 確定——實際上是配置的 LocaleResolverLocaleContextResolver

java.util.TimeZone, java.time.ZoneId

與當前請求關聯的時區,由 LocaleContextResolver 確定。

java.io.OutputStream, java.io.Writer

用於訪問 Servlet API 公開的原始響應體。

java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap

用於訪問錯誤響應的模型。始終為空。

RedirectAttributes

指定在重定向情況下使用的屬性(即要附加到查詢字串)和臨時儲存的 flash 屬性,直到重定向後的請求。請參閱重定向屬性Flash 屬性

@SessionAttribute

用於訪問任何會話屬性,與因類級別 @SessionAttributes 宣告而儲存在會話中的模型屬性形成對比。有關更多詳細資訊,請參閱@SessionAttribute

@RequestAttribute

用於訪問請求屬性。有關更多詳細資訊,請參閱@RequestAttribute

返回值

@ExceptionHandler 方法支援以下返回值

返回值 描述

@ResponseBody

返回值透過 HttpMessageConverter 例項轉換並寫入響應。請參閱@ResponseBody

HttpEntity<B>, ResponseEntity<B>

返回值指定整個響應(包括 HTTP 頭和正文)透過 HttpMessageConverter 例項轉換並寫入響應。請參閱ResponseEntity

ErrorResponse, ProblemDetail

要呈現帶有正文詳細資訊的 RFC 9457 錯誤響應,請參閱錯誤響應

字串

要透過 ViewResolver 實現解析並與隱式模型(透過命令物件和 @ModelAttribute 方法確定)一起使用的檢視名稱。處理程式方法還可以透過宣告 Model 引數(前面已描述)來程式設計地豐富模型。

View

要與隱式模型(透過命令物件和 @ModelAttribute 方法確定)一起渲染的 View 例項。處理程式方法還可以透過宣告 Model 引數(前面已描述)來程式設計地豐富模型。

java.util.Map, org.springframework.ui.Model

要新增到隱式模型的屬性,檢視名稱透過 RequestToViewNameTranslator 隱式確定。

@ModelAttribute

要新增到模型的屬性,檢視名稱透過 RequestToViewNameTranslator 隱式確定。

請注意,@ModelAttribute 是可選的。請參閱此表末尾的“任何其他返回值”。

ModelAndView 物件

要使用的檢視和模型屬性,以及可選的響應狀態。

void

如果一個方法具有 void 返回型別(或 null 返回值),並且它也具有 ServletResponseOutputStream 引數或 @ResponseStatus 註解,則認為它已完全處理了響應。如果控制器已進行了積極的 ETaglastModified 時間戳檢查(有關詳細資訊,請參閱控制器),則也適用。

如果以上都不成立,void 返回型別也可以表示 REST 控制器的“無響應體”或 HTML 控制器的預設檢視名稱選擇。

任何其他返回值

如果返回值與上述任何一項都不匹配,並且不是簡單型別(由 BeanUtils#isSimpleProperty 確定),則預設情況下,它被視為要新增到模型的模型屬性。如果它是簡單型別,則保持未解析。

© . This site is unofficial and not affiliated with VMware.