Web中的错误处理,就是在系统抛出异常之后,HTTP响应使用什么状态码,显示什么页面。
默认的处理方式
如果系统中抛出异常,Tomcat会将响应状态码设置成500,并自动生成一个含有错误堆栈信息的页面。
如果想让Tomcat使用其它响应状态码,可以使用Response.sendError(int sc, String msg)
改变状态码。
web.xml配置
Tomcat自动生成的页面显然不能够满足异常处理的需要,最常见的一个需求就是,自定义错误处理页面。
可以通过web.xml配置某个响应状态码对应的错误页面。
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
也可以配置某个异常对应的错误页面。
<error-page>
<exception-type>java.lang.NullException</exception-type>
<location>/error.jsp</location>
</error-page>
Spring MVC错误处理
Spring MVC提供了多个HandlerExceptionResolver
处理异常。根据以下顺序依次进行处理。
- SimpleMappingExceptionResolver:可定义异常类型对应的错误页面
- DefaultHandlerExceptionResolver:处理Spring MVC自己抛出的异常。另外还有
ResponseEntityExceptionHandler
- ResponseStatusExceptionResolver:处理使用
@ResponseStatus
注解了的异常 - ExceptionHandlerExceptionResolver:调用
@ExceptionHandler
方法处理异常
SimpleMappingExceptionResolver
用于定义异常类型和对应的错误页面
<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.dao.DataIntegrityViolationException">data_integrity_violation_exception</prop>
</props>
</property>
</bean>
自定义HandlerExceptionResolver
自定以HandlerExceptionResolver
处理异常,可返回以下三种结果。
- 直接返回指向错误页面的
ModelAndView
- 如果错误被解决,则返回空的
ModelAndView
- 如果错误无法处理,则返回
null
,由下一个HandlerExceptionResolver
继续处理
容器错误页面
如果所有的HandlerExceptionResolver
都无法处理异常,这个异常将交给Servlet容器处理。可以在web.xml里定义错误页面的地址。
<error-page>
<location>/error</location>
</error-page>
再定义Controller进行处理:
@RestController
public class ErrorController {
@RequestMapping(path = "/error")
public Map<String, Object> handle(HttpServletRequest request) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("status", request.getAttribute("javax.servlet.error.status_code"));
map.put("reason", request.getAttribute("javax.servlet.error.message"));
return map;
}
}
Spring Boot 错误处理
Spring Boot默认用BasicErrorController
处理/error
错误。对于浏览器请求,会根据状态码自动对应到相应的模板。如:
- error/400.html
- error/403.html
- error/404.html
- error/5xx.html
同时会根据DefaultErrorAttributes
提供以下数据:
- timestamp - The time that the errors were extracted
- status - The status code
- error - The error reason
- exception - The class name of the root exception (if configured)
- message - The exception message (if configured)
- errors - Any ObjectErrors from a BindingResult exception (if configured)
- trace - The exception stack trace (if configured)
- path - The URL path when the exception was raised
Spring Security 错误处理
Spring Security 使用ExceptionTranslationFilter
捕获AccessDeniedException
异常,由AccessDeniedHandlerImpl
处理。
如果没有设置errorPage
,则直接调用response.sendError()
。否则转发到错误页面。
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
if (response.isCommitted()) {
logger.trace("Did not write to response since already committed");
return;
}
if (this.errorPage == null) {
logger.debug("Responding with 403 status code");
response.sendError(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase());
return;
}
// Put exception into request scope (perhaps of use to a view)
request.setAttribute(WebAttributes.ACCESS_DENIED_403, accessDeniedException);
// Set the 403 status code.
response.setStatus(HttpStatus.FORBIDDEN.value());
// forward to error page.
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.format("Forwarding to %s with status code 403", this.errorPage));
}
request.getRequestDispatcher(this.errorPage).forward(request, response);
}