异常处理
如果在请求映射过程中发生异常,或者从请求处理器(如@Controller)抛出异常,DispatcherServlet会委托给一系列HandlerExceptionResolver bean来处理该异常并提供替代的处理方式,通常表现为一个错误响应。
下表列出了可用的 HandlerExceptionResolver 实现:
表1. HandlerExceptionResolver实现
HandlerExceptionResolver | 描述 |
|---|---|
SimpleMappingExceptionResolver | 实现异常类名与错误页面名称之间的映射。适用于在浏览器应用程序中渲染错误页面。 |
| DefaultHandlerExceptionResolver | 解决由Spring MVC引发的异常,并将这些异常映射为HTTP状态码。另请参阅替代方案ResponseEntityExceptionHandler以及错误响应。 |
ResponseStatusExceptionResolver | 解决带有@ResponseStatus注解的异常,并根据该注解中的值将这些异常映射为相应的HTTP状态码。 |
ExceptionHandlerExceptionResolver | 通过调用@Controller或@ControllerAdvice类中的@ExceptionHandler方法来处理异常。详见@ExceptionHandler方法。 |
解析器链
你可以通过在Spring配置中声明多个HandlerExceptionResolver bean,并根据需要设置它们的order属性来构建一个异常解析器链。order属性的值越大,该异常解析器的执行顺序就越靠后。
HandlerExceptionResolver 的契约规定它可以返回:
- 一个指向错误页面的
ModelAndView对象。 - 如果异常在解析器(resolver)内部被处理,则返回一个空的
ModelAndView对象。 - 如果异常仍未得到解决,将返回
null,以便后续的解析器继续尝试处理;如果最终异常仍然存在,该异常将被允许向上传递至Servlet容器。
MVC 配置 会自动声明针对默认 Spring MVC 异常、带有 @ResponseStatus 注解的异常以及支持 @ExceptionHandler 方法的内置解析器。你可以自定义该列表或对其进行替换。
容器错误页面
如果某个异常没有被任何HandlerExceptionResolver解决,因此被允许继续传播;或者如果响应状态被设置为错误状态(即4xx、5xx),Servlet容器可以渲染一个HTML格式的默认错误页面。要自定义容器的默认错误页面,你可以在web.xml中声明一个错误页面映射。以下示例展示了如何进行操作:
<error-page>
<location>/error</location>
</error-page>
根据前面的例子,当出现异常或者响应具有错误状态时,Servlet容器会在容器内部将请求转发到配置好的URL(例如 /error)。然后这个请求会被 DispatcherServlet 处理,它可能会将请求映射到一个 @Controller 类。该 @Controller 可以被实现为返回一个带有模型的错误视图名称,或者渲染一个 JSON 响应,如下例所示:
- Java
- Kotlin
@RestController
public class ErrorController {
@RequestMapping(path = "/error")
public Map<String, Object> handle(HttpServletRequest request) {
Map<String, Object> map = new HashMap<>();
map.put("status", request.getAttribute("jakarta.servlet.error.status_code"));
map.put("reason", request.getAttribute("jakarta.servlet.error.message"));
return map;
}
}
@RestController
class ErrorController {
@RequestMapping(path = ["/error"])
fun handle(request: HttpServletRequest): Map<String, Any> {
val map = HashMap<String, Any>()
map["status"] = request.getAttribute("jakarta.servlet.error.status_code")
map["reason"] = request.getAttribute("jakarta.servlet.error.message")
return map
}
}
Servlet API 并没有提供在 Java 中创建错误页面映射的方法。不过,你可以同时使用 WebApplicationInitializer 和一个简化的 web.xml 文件。