正常的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會幫你「撿起來」。