异常
@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 确定),默认情况下,它会被视为要添加到模型的模型属性。如果它是简单类型,则保持未解析状态。 |