===================================
視圖函數返回 status code 的方式
===================================
Spring 有一個專門的枚舉類型 HttpStatus, 比如 HttpStatus.NOT_FOUND
1. 視圖函數返回 ResponseEntity 類型的對象.
2. 在 exception 類加注解 @ResponseStatus, 一旦視圖函數拋出這個異常, Spring 就會自動返回設定的 status code.
3. 在視圖函數上加 @ResponseStatus, 只要該函數沒有報異常, Spring 就會自動返回設定的 status code.
===================================
方式一: 在業務 Controller 內部增加異常視圖函數
===================================
在我們的 Controller 類中專門加一個或幾個處理異常的視圖函數, 這些異常處理視圖需要加 @ExceptionHandler 注解.
將 @ExceptionHandler 注解的加到異常視圖函數上, 也可以將 @ResponseStatus 注解一起加上. 異常視圖函數的參數和普通視圖函數的參數不一樣, 能傳入 Exception/HttpServletRequest/HttpServletResponse/HttpSession/Principle 等參數, 但是不能傳入 Model 之類的參數, 視圖方法的返回類型可以是: void/String 或 ModelAndView.
該方式缺點是: 這種異常處理僅僅在本 controller 有效, 不太好做到通用的異常處理. 當然也可設計一個通用的 Controller base 類, 在 base 類中加上異常處理視圖函數, 所有業務 controller 都繼承這個基類.
===================================
方式二: 實現一個基於 HandlerExceptionResolver 接口的 bean
===================================
在 Spring Web 項目中, 如果我們的 bean 實現了 HandlerExceptionResolver 接口, 一旦某個 controller 拋出異常, 這個 bean 會截獲並處理異常.
該方法的缺點是: 不太好返回具體的報錯內容.
一般情況下, 我們的 bean 並不直接實現 HandlerExceptionResolver 接口, 而是繼承 SimpleMappingExceptionResolver 類.
通常做法是, 我們先自定義一個 SimpleMappingExceptionResolver 子類, 實現 doResolveException() 方法, 然后在 Mvc Configruation 配置類中, 聲明一個名為 simpleMappingExceptionResolver 的 bean. 之所以繼承 SimpleMappingExceptionResolver 類, 是因為它已經實現了很多有用的功能, 我們直接使用即可, 比如:
1. 設定 exception 和 view 視圖的 mapping 關系等
2. 設置缺省的異常處理視圖名
3. 異常 log 功能 (需啟用)
除了 SimpleMappingExceptionResolver 類之外, Spring 還有其他幾個 HandlerExceptionResolver 實現類, 作用分別是:
1. ExceptionHandlerExceptionResolver, 用來截獲標注 @ExceptionHandler 注解的視圖函數異常, @ExceptionHandler 加在專門的異常處理視圖函數上.
2. ResponseStatusExceptionResolver, 用來截獲和 @ResponseStatus 注解相關的異常, @ResponseStatus 可以加在 Exception 類或視圖函數上.
3. DefaultHandlerExceptionResolver, 用來截獲標准的 Spring exception, 並將異常轉成相應 Http status code, 比如 404 等.
===================================
方式三: 使用 @ControllerAdvice 注解聲明一個異常處理類
===================================
@ControllerAdvice 是 Spring 3.2 之后引入的. 在一個業務 controller 拋出異常后, @ControllerAdvice 標注的類會截獲該異常. @ControllerAdvice 還有一個兄弟 @RestControllerAdvice 注解.
@ControllerAdvice 標注在類上, 具體異常視圖函數仍然通過加 @ExceptionHandler 注解聲明的, 該視圖函數能傳入 Exception/HttpServletRequest/HttpServletResponse/HttpSession/Principle 等參數, 但是不能傳入 Model 之類的參數, 視圖方法的返回類型可以是: void/String 或 ModelAndView.
@ControllerAdvice 優點有:
1. @ControllerAdvice 處理機制能接管所有 controller 類的異常, 更容易實現全局統一的異常處理.
2. 可以自由組合不同的 Exception 的異常處理視圖.
3. 異常視圖函數可以用 @ResponseStatus 注解指定返回碼. 視圖函數返回類型可以是 void/String 或 ModelAndView, 所以很容易返回報錯內容.
4. 我們 @ControllerAdvice 注解類最好繼承至 ResponseEntityExceptionHandler, 該 ResponseEntityExceptionHandler 類已經內置了很多標准異常的異常處理視圖, 所以我們不需要再關心標准異常的處理, 只需要關心和業務相關的異常即可.
===================================
Spring Boot 默認的出錯處理
===================================
Spring MVC 沒有提供缺省的 fall-back 出錯頁, 而 Spring Boot 默認情況下就有很完善的報錯處理機制, 一旦視圖函數報錯, Spring boot 先看有沒有映射 /error 路徑的視圖, 有的話渲染該視圖, 沒有的話, 會展現 "Whitelabel Error Page", 該頁面能顯示出 Status code 和詳細的報錯信息. 如果是 Restful 請求的話, Whitelabel 報錯也是 json 格式的反饋.
比如:
$> curl -H "Accept: application/json" http://localhost:8080/no-such-page
返回是:
{"timestamp":"2018-04-11T05:56:03.845+0000","status":404,"error":"Not Found","message":"No message available","path":"/no-such-page"}
默認缺省的異常處理路徑是 /error, 也可以通過 server.error.path 屬性修改.
Spring Boot 之所以有一個 whitelabel error頁面, 是因為 Spring Boot 會注入一個 BasicErrorController 用來處理異常, 相關的 application 屬性有:
server.error.include-exception=false # Include the "exception" attribute. server.error.include-stacktrace=never # When to include a "stacktrace" attribute. server.error.path=/error # Path of the error controller. server.error.whitelabel.enabled=true # Whether to enable the default error page displayed in browsers in case of a server error.
如果缺省的 whitelabel 頁面不能滿足需求, 可以簡單定制 page 頁, 在 resources 目錄下, 新建一個 error.html 模板文件, 在文件中, 可以使用下面的變量.
${timestamp} 、 ${path} 、 ${error} 、 ${status} 、 ${message} 、 ${exception}、 ${trace}
比如要對 404 訪問做日志, 我們可以仿造 BasicErrorController 寫一個自己的 ErrorController 類. Spring boot 會自動這個 ErrorController 實例代替缺省的 ErrorController.
===================================
推薦的做法:
===================================
1. 在一個項目中, 僅使用一種異常處理方法, 不要組合使用, 因為有可能和我們的預期不一致.
2. 推薦使用全局性的異常處理, 而不是 Controller 級別的異常處理; 推薦 @ControllerAdvice, 而不是基於 HandlerExceptionResolver 接口的 bean.
3. @ControllerAdvice 注解類最好繼承至 ResponseEntityExceptionHandler, 該 ResponseEntityExceptionHandler 類已經內置了很多標准異常的異常處理視圖, 所以我們不需要再關心標准異常的處理, 只需要關心和業務相關的異常即可.
需要說明的是, 只有進入 Controller 的異常, @ControllerAdvice 才能捕獲, 對於攔截器的異常和 url寫錯這類404 這樣的異常, 是捕獲不到的, 當然一般情況下我們也沒有必要處理這些異常, 如果一定要對 404 訪問做日志, 我們可以仿造 BasicErrorController 寫一個自己的 ErrorController 類.
下面是一個簡單的示意代碼:
@ControllerAdvice class GlobalExceptionHandler extends ResponseEntityExceptionHandler { // 針對不同的異常, 定義其處理視圖函數 // some view functions // 對應 uncaughted 異常, 使用下面這個視圖函數來兜底 public static final String DEFAULT_ERROR_VIEW = "error"; @ExceptionHandler(value = Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest req, HttpServletResponse rep, Exception e) throws Exception { //針對 500 錯誤, 需要記錄 error 日志. logger.error(e.getMessage(),e); if (req.getHeader("Accept").contains("application/json")) { //write json to HttpServletResponse return null; } else{ ModelAndView mav = new ModelAndView(); mav.addObject("exception", e); mav.addObject("url", req.getRequestURL()); mav.setViewName(DEFAULT_ERROR_VIEW); return mav; } } }
===================================
參考:
===================================
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
https://blog.csdn.net/aiyaya_/article/details/78725755
http://blog.didispace.com/springbootexception/
https://blog.csdn.net/chinrui/article/details/71036544
http://tengj.top/2018/05/16/springboot13/