Spring Boot學習筆記-錯誤處理及自定義

正常的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處理。

覆蓋默認的錯誤處理方式

默認錯誤處理機制的響應內容格式不必定是你相中的。理由可能以下:

  1. 「Whitelabel Error Page」頁面的樣式太單調,用戶體驗很差。
  2. Json格式的結果字符串不統一,與你配合的前端人員更但願統一格式,好作統一的顯示處理。好比筆者以前與前端人員配合時統一指定響應結果格式爲「{status:true,msg:"xxx",data:{xxx}}」,但Spring Boot的Json格式是{status:500,message:"error occur",path:"/exception"......}。

那麼你可能更指望能夠修改默認的處理方式,改變響應內容格式。Spring Boot開發指南上給出了幾種方法。

  1. 自定義一個bean,實現ErrorController接口,那麼默認的錯誤處理機制將再也不生效。
  2. 自定義一個bean,繼承BasicErrorController類,使用一部分現成的功能,本身也能夠添加新的public方法,使用@RequestMapping及其produces屬性指定新的地址映射。
  3. 自定義一個ErrorAttribute類型的bean,那麼仍是默認的兩種響應方式,只不過改變了內容項而已。

先說下第三種方法,其實查看BasicErrorController源碼,響應結果不管是html仍是json,內容源都是ErrorAttribute。第三種方法只能改變內容,卻改變不了格式,特別是html頁面的樣式,筆者就不予考慮了。

其實指南上只是輕描淡寫了幾種方法,沒有很好的示例,不知道是否是指南的做者認爲Spring Boot默認的錯誤處理機制已經很適用了。不管第一種仍是第二種辦法,都須要你看下BasicErrorController的繼承體系及實現,由於BasicErrorController也是實現了ErrorController。筆者也是着實費了一番功夫。

採用第一種方式你能夠具備徹底的控制權,你能夠摒棄默認的「Whitelabel Error Page」,指定本身的視圖及視圖樣式,你能夠指定響應的Json格式內容等等,由於BasicErrorController再也不起做用,能夠參考這裏

因爲筆者參照了BasicErrorController的源碼,感受第二種方法可能更簡便些,因此實現了第二種方法。第二種方法的思路其實就是你能夠經過繼承,利用BasicErrorController已有的功能,或者進行擴展。

那麼如何覆蓋默認的處理行爲呢(雖然是自定義bean,但由於是繼承,沒有覆蓋的話仍是會採用默認的處理行爲)?大體有兩種思路。

  1. 按照指南上所述,你能夠新建public方法,使用@RequestMapping及其produces屬性,例如@RequestMapping(produces="application/json"),那麼只要請求頭中包含「Accept: application/json」,則會映射到你的方法進行處理。看到這兒你可能會疑惑,why?仍是建議你先看下BasicErrorController源碼
  2. 由於是繼承,因此對BasicErrorController默認兩種錯誤處理方式的方法進行override。

筆者但願徹底覆蓋及可控,因此選擇了第二種思路。筆者自定義了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會幫你「撿起來」。

相關文章
相關標籤/搜索