Exceptions
@Controller
和 @ControllerAdvice 类可以具有 @ExceptionHandler
方法来处理控制器方法的异常,如下例所示:
include-code::./SimpleController[indent=0]
Exception Mapping
该异常可能与正在传播的顶级异常(例如直接抛出 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 机制上。
Media Type Mapping
除了异常类型之外,@ExceptionHandler
方法还可以声明可生产的媒体类型。这允许根据 HTTP 客户端请求的媒体类型(通常在“Accept”HTTP 请求头中)来优化错误响应。
应用程序可以直接在注释中为同一异常类型声明可生产的媒体类型:
/// The provided media type to update the metadata of./// /// /// The operation to be performed./// /// /// The of the fields to update./// /// /// A cancellation token to observe./// /// /// The updated ./// /// /// is null ./// /// /// is null ./// /// /// is not one of the allowed values./// /// /// An error occurred while making the API request./// /// /// An error occurred while making the gRPC request./// [Google.Api.HttpBody(Body = "*" hljs" data-lang="csharp/// /// Updates metadata of a media type./// /// /// The provided media type to update the metadata of./// /// /// The operation to be performed./// /// /// The of the fields to update./// /// /// A cancellation token to observe./// /// /// The updated ./// /// /// is null ./// /// /// is null ./// /// /// is not one of the allowed values./// /// /// An error occurred while making the API request./// /// /// An error occurred while making the gRPC request./// [Google.Api.HttpBody(Body = "*"">在此处,方法处理相同的异常类型,但不会因重复而被拒绝。相反,请求“application/json”的 API 客户端将接收一个 JSON 错误,而浏览器会获取一个 HTML 错误视图。每个 `@ExceptionHandler` 注释可以声明多种可生产的媒体类型,错误处理阶段的内容协商将决定使用哪种内容类型。
[id="mvc-ann-exceptionhandler-args"]
== Method Arguments
[role="small"][.small]xref:web/webflux/controller/ann-exceptions.adoc#webflux-ann-exceptionhandler-args[See equivalent in the Reactive stack]
`@ExceptionHandler` 方法支持以下参数:
[cols="1,2"]
|===
| Method argument | Description
| Exception type | 访问引发的异常。
| `HandlerMethod` | 访问引发异常的控制器方法。
| `WebRequest`, `NativeWebRequest` | 无需直接使用 Servlet API 即可访问请求参数和请求和会话属性。
| `jakarta.servlet.ServletRequest`, `jakarta.servlet.ServletResponse` | 选择任何特定请求或响应类型(例如 `ServletRequest` 或 `HttpServletRequest` 或 Spring 的 `MultipartRequest` 或 `MultipartHttpServletRequest`)。
| `jakarta.servlet.http.HttpSession` | 强制需要会话。作为结果,此类参数绝不会是 `null`。请注意,会话访问不是线程安全的。如果允许多个请求并发访问会话,请考虑将 `RequestMappingHandlerAdapter` 实例的 `synchronizeOnSession` 标志设为 `true`。
| `java.security.Principal` | 当前经过鉴权的用户——如果已知,可能是一个特定的 `Principal` 实现类。
| `HttpMethod` | 请求的 HTTP 方法。
| `java.util.Locale` | 当前请求语言环境,由最具体的 `LocaleResolver` 决定,实际上是配置的 `LocaleResolver` 或 `LocaleContextResolver`。
| `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` | 指定在重定向情况下要使用的属性 -(即要追加到查询字符串) 和闪存属性,以便临时存储到重定向后的请求。请参见 xref:web/webmvc/mvc-controller/ann-methods/redirecting-passing-data.adoc[Redirect Attributes] 和 xref:web/webmvc/mvc-controller/ann-methods/flash-attributes.adoc[Flash Attributes]。
| `@SessionAttribute` | 要访问某些会话属性(与存储在会话中的模型属性不同,如下所示:类级 `@SessionAttributes` 声明。请参阅 xref:web/webmvc/mvc-controller/ann-methods/sessionattribute.adoc[`@SessionAttribute` 了解详情)。
| `@RequestAttribute` | 如需访问请求属性。更多详细信息,请参见 xref:web/webmvc/mvc-controller/ann-methods/requestattrib.adoc[`@RequestAttribute`。
|===
[id="mvc-ann-exceptionhandler-return-values"]
== Return Values
[role="small"][.small]xref:web/webflux/controller/ann-exceptions.adoc#webflux-ann-exceptionhandler-return-values[See equivalent in the Reactive stack]
`@ExceptionHandler` 方法支持以下返回值:
[cols="1,2"]
|===
| Return value | Description
| `@ResponseBody` | 返回值通过 `HttpMessageConverter` 实例进行转换并写入响应。请参见 xref:web/webmvc/mvc-controller/ann-methods/responsebody.adoc[`@ResponseBody`]。
| `HttpEntity<B>`, `ResponseEntity<B>` | 返回值指定将通过 `HttpMessageConverter` 实例转换完整响应(包括 HTTP 头和正文)并写到该响应中。参见 xref:web/webmvc/mvc-controller/ann-methods/responseentity.adoc[ResponseEntity]。
| `ErrorResponse` | 如需渲染正文中带有详细信息的 RFC 9457 错误响应,请参见 xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]。
| `ProblemDetail` | 如需渲染正文中带有详细信息的 RFC 9457 错误响应,请参见 xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]。
| `String` | 要使用 `ViewResolver` 实现解析的视图名称,并与隐含模型 — 通过命令对象和 `@ModelAttribute` 方法确定一起使用。处理程序方法还可以通过声明 `Model` 参数(如前所述)以编程方式丰富模型。
| `View` | 一个 `View` 实例,用于与隐含模型 — 通过命令对象和 `@ModelAttribute` 方法确定一起进行呈现。处理程序方法还可以通过声明 `Model` 参数(如前所述)以编程方式丰富模型。
| `java.util.Map`, `org.springframework.ui.Model` | 通过 `RequestToViewNameTranslator` 隐含确定的要添加到隐含模型中的属性。
| `@ModelAttribute` | 要通过 `RequestToViewNameTranslator` 隐含确定的模型中的属性。请注意,`@ModelAttribute` 是可选的。请参阅此表结尾处的“`Any other return value`”。
| `ModelAndView` object | 要使用的视图和模型属性以及一个可选响应状态。
| `void` | 如果某个方法具有 `void` 返回类型(或 `null` 返回值),则如果该方法还具有 `ServletResponse` 或 `OutputStream` 参数,或 `@ResponseStatus` 注解,则该方法将被视为已完全处理了响应。如果控制器已对 ETag 或 `ETag` 时间戳进行过肯定检查(有关详细信息,请参见 `lastModified`),则情况也是如此。如果上述情况均不成立,则 `void` 返回类型对于 REST 控制器还可以表示“无响应正文”,或对于 HTML 控制器还可以表示默认视图名称选择。
| Any other return value | 如果返回值与上述任何情况都不匹配,并且不是简单类型(由 {spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty] 确定),则默认情况下将其视为要添加到模型的模型属性。如果它是一个简单类型,它仍然未解决。
|===