Web中的错误处理,就是在系统抛出异常之后,HTTP响应使用什么状态码,显示什么页面。
如果系统中抛出异常,Tomcat会将响应状态码设置成500,并自动生成一个含有错误堆栈信息的页面。
如果想让Tomcat使用其它响应状态码,可以使用Response.sendError(int sc, String msg)
改变状态码。
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提供了多个HandlerExceptionResolver
处理异常。根据以下顺序依次进行处理。
ResponseEntityExceptionHandler
@ResponseStatus
注解了的异常@ExceptionHandler
方法处理异常用于定义异常类型和对应的错误页面
<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
处理异常,可返回以下三种结果。
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默认用BasicErrorController
处理/error
错误。对于浏览器请求,会根据状态码自动对应到相应的模板。如:
同时会根据DefaultErrorAttributes
提供以下数据:
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);
}