正常的Web应用开发时,须要考虑到应用运行发生异常时或出现错误时如何来被处理,例如捕获必要的异常信息,记录日志方便往后排错,友好的用户响应输出等等。html
固然应用发生错误,有多是应用自身的问题,也有多是客户端操做的问题。前端
Spring Boot默认提供了一种错误处理机制。java
默认状况下,Spring Boot为两种状况提供了不一样的响应方式。git
一种是浏览器客户端访问应用发生错误时,通常状况下浏览器默认发送的请求头中Accept: text/html(固然你更改了就另当别论了),因此Spring Boot默认会响应一个html文档内容,称做“Whitelabel Error Page”。github
另外一种是机器客户端访问应用发送错误时,Spring Boot会响应Json格式内容。这种状况更常见于利用第三方的Http工具请求接口时。web
若是看过源码,会发现两种方式输出用到的内容项是同样的,只不过第一种方式用html格式显示,第二种方式使用了Json格式。spring
Spring Boot提供这个错误处理机制靠自动配置的BasicErrorController类。若是好奇Spring Boot是如何实现这种机制的。能够参看下BasicErrorController源码以及SpringMVC请求映射匹配规则,点这里。json
上述的默认错误处理机制是一种通用的作法,你可能更指望细化一些处理,对于某些错误你可能想特殊对待。Spring Boot提供了一种方式,笔者认为这种方式是“容器级别”的操做。以下:浏览器
@Configuration public class ContainerConfig { @Bean public EmbeddedServletContainerCustomizer containerCustomizer(){ return new MyCustomizer(); } private static class MyCustomizer implements EmbeddedServletContainerCustomizer { @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500")); } } }
笔者新增了一个配置类,里面定义了一个bean,主要目的在于针对响应码为500的错误,采用笔者自定义的方式处理。以下:app
@RestController public class ExceptionController { @RequestMapping("/exception") public void catchException() { throw new RuntimeException("error occur"); } @RequestMapping("/500") public String showServerError() { return "server error"; } }
那么浏览器中结果以下:
很明显这种方式依赖于响应状态码进行定制。看到这里你应该会眼熟,还记得web.xml文件里的配置吗?
<error-page> <exception-type>java.lang.Throwable</exception-type> <location>/error/500</location> </error-page> <error-page> <error-code>500</error-code> <location>/error/500</location> </error-page> <error-page> <error-code>401</error-code> <location>/error/401</location> </error-page> <error-page> <error-code>403</error-code> <location>/error/403</location> </error-page>
笔者认为,Spring Boot的默认错误处理机制,包括自定义的错误页面,与原来的web.xml中的error-page配置是相通的,只不过Spring Boot应用默认使用内嵌Servlet容器,web.xml不见了而已。查看源码你会发现,Spring Boot在应用发生错误时会转向"/error"请求,即交由BasicErrorController处理。
默认错误处理机制的响应内容格式不必定是你相中的。理由可能以下:
那么你可能更指望能够修改默认的处理方式,改变响应内容格式。Spring Boot开发指南上给出了几种方法。
先说下第三种方法,其实查看BasicErrorController源码,响应结果不管是html仍是json,内容源都是ErrorAttribute。第三种方法只能改变内容,却改变不了格式,特别是html页面的样式,笔者就不予考虑了。
其实指南上只是轻描淡写了几种方法,没有很好的示例,不知道是否是指南的做者认为Spring Boot默认的错误处理机制已经很适用了。不管第一种仍是第二种办法,都须要你看下BasicErrorController的继承体系及实现,由于BasicErrorController也是实现了ErrorController。笔者也是着实费了一番功夫。
采用第一种方式你能够具备彻底的控制权,你能够摒弃默认的“Whitelabel Error Page”,指定本身的视图及视图样式,你能够指定响应的Json格式内容等等,由于BasicErrorController再也不起做用,能够参考这里。
因为笔者参照了BasicErrorController的源码,感受第二种方法可能更简便些,因此实现了第二种方法。第二种方法的思路其实就是你能够经过继承,利用BasicErrorController已有的功能,或者进行扩展。
那么如何覆盖默认的处理行为呢(虽然是自定义bean,但由于是继承,没有覆盖的话仍是会采用默认的处理行为)?大体有两种思路。
笔者但愿彻底覆盖及可控,因此选择了第二种思路。笔者自定义了MyErrorController,继承于BasicErrorController,注意必定要添加@Controller,否则Spring没法感知自定义的bean,继承于BasicErrorController仍是会起做用!具体实现可猛戳这里。
因为笔者使用了自定义的视图,须要添加以下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
Spring Boot默认视图模板路径为“src/main/resources/templates”。
运行结果以下:
Spring Boot提供的ErrorController是一种全局性的容错机制。你还可使用SpringMVC提供的@ControllerAdvice。
如字面意思,@ControllerAdvice是切面技术的应用,容许你对Controller中抛出的某个或某些异常进行捕获并响应输出。用法以下:
@ControllerAdvice public class DefaultExceptionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExceptionHandler.class); //日志记录器 @ExceptionHandler({MissingServletRequestParameterException.class, TypeMismatchException.class, IllegalArgumentException.class, IllegalStateException.class}) @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ResponseBody public JsonResult conversionErrorHandler(Exception ex) { //记录日志 LOGGER.error("参数异常捕获", ex); return new JsonResult(false, ErrorCode2Msg.getMessage(10004)); } }
笔者以往的开发习惯,使用@ControllerAdvice捕获应用级别的异常,使用web.xml中的error-page配置处理容器级别的报错。假设定义的过滤器抛出的异常,@ControllerAdvice是没法处理的。
改用Spring Boot后,@ControllerAdvice没有捕获的异常,ErrorController会帮你“捡起来”。