文章目錄
-
- 一、錯誤處理原理分析
- 二、進行錯誤處理
-
- 1. 使用SpringBoot的自動配置原理進行異常處理
-
- 1)、傳回一個錯誤頁面,如404、500等。
-
- 有模闆引擎的情況(可以用于渲染頁面)
- 沒有模闆引擎的情況
- 2)、傳回對應的json串
- 3)、自定義頁面傳回資訊
- 2. 使用AOP的異常通知進行處理(推薦)
- 3. 兩種方法對比:
一、錯誤處理原理分析
使用SpringBoot建立的web項目中,當我們請求的頁面不存在(http狀态碼為404),或者器發生異常(http狀态碼一般為500)時,SpringBoot就會給我們傳回錯誤資訊。
也就是說,在SpringBoot的web項目中,會自動建立一個/error的錯誤接口,來傳回錯誤資訊。但是針對不同的通路方式,會有以下兩種不同的傳回資訊。這主要取決于你通路時的http頭部資訊的
Accept
這個值來指定你可以接收的類型有哪些
- 使用浏覽器通路時的頭資訊及其傳回結果
Accept: text/html
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcsQXYtJ3bm9CXldWYtlWPzNXZj9mcw1ycz9WL49zdaVnQuxUMJR1TxUkaNlXSE50dBpWT3lkeM5WMXFmdJhlWw4EWZRXO5RGaKNDTuFzVhRHOyolasd0Y2lEWaJjVH5UdshkY2BjMipWNTpFbShVYulTeMZTTINGMShUYvwlbj5yZtlmbkN3YuQnclZnbvN2Ztl2Lc9CX6MHc0RHaiojIsJye.jpg)
- 使用其他裝置,如手機用戶端等通路時頭部資訊及其傳回結果(一般是在前後端分離的架構中)
Accept: */*
二、進行錯誤處理
處理異常主要有兩種方式:
1. 使用SpringBoot的自動配置原理進行異常處理
SpringBoot自動配置了一個類
ErrorMvcAutoConfiguration
來處理處理異常,有興趣的可以去看一下,然後在這個類中定義一個錯誤的BasicErrorController類,主要代碼有如下:
@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
/**
* 錯誤的頁面響應
*/
@RequestMapping(produces = {"text/html"})
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
// 得到一個modelAndView對象
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
return modelAndView != null ? modelAndView : new ModelAndView("error", model);
}
/**
* 錯誤的json響應
*/
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = this.getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity(status);
} else {
Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
return new ResponseEntity(body, status);
}
}
}
多的代碼就不深究了,感興趣的可以去看一下。上邊的代碼也就是說,針對不同的請求方式,會傳回不同的結果,其關鍵在于
@RequestMapping
注解的
produces = {"text/html"}
屬性上
1)、傳回一個錯誤頁面,如404、500等。
-
有模闆引擎的情況(可以用于渲染頁面)
項目中使用的了模闆引擎,如:thymeleaf 、freemarker等做為頁面的渲染時。在templates建立/error檔案夾并添加錯誤的狀态碼對應的.html檔案,如下圖:
這裡的404和500就是确定的錯誤狀态碼,而4xx表示其他的4開頭的錯誤,如400,401等。當然可以為每一個狀态碼都設定對應的錯誤頁面,但是這樣做,并沒有什麼好處,是以就直接使用4xx.html這樣的泛指代替了。
可以在我們錯誤頁面中擷取到如下資訊(就是ModelAndView對象中的内容):
字段名 | 說明 |
---|---|
timstamp | 時間戳 |
status | 錯誤狀态碼 |
error | 錯誤提示 |
exception | 異常對象 |
message | 異常消息 |
path | 頁面路徑 |
細心的小夥伴會發現,這個其實就是當你用手機請求時傳回的json内容
比如:在代碼中加入上邊資訊,然後在在後端寫一個錯誤代碼:
@RequestMapping("haserror")
@ResponseBody
public Object myError(){
int i =10/0;
return "something is error";
}
這是一個錯誤頁面:
<ul>
<li>錯誤狀态碼:[[${status}]]</li>
<li>錯誤消息:[[${error}]]</li>
<li>異常對象:[[${exception}]]</li>
<li>異常消息:[[${message}]]</li>
<li>目前時間:[[${timestamp}]]</li>
</ul>
-
沒有模闆引擎的情況
當項目中沒有使用模闆引擎的時候,就将整個error檔案夾移到static檔案夾下就可以了。
不過此時并不能擷取上邊的那些資訊了,因為這本就是靜态資源,沒有模闆引擎進行渲染
2)、傳回對應的json串
這個并沒有什麼好說的,傳回的就是一個json字元串。格式如下:
{
"timestamp": "2020-04-22T16:13:37.506+0000",
"status": 500,
"error": "Internal Server Error",
"message": "/ by zero",
"path": "/hello/haserror",
"reason": "完了,你寫的代碼又産生了一次線上事故"
}
3)、自定義頁面傳回資訊
這才是最重要的内容,因為這個資訊不僅是做為json傳回的,也是可以在上邊的錯誤頁面中拿到,也可以直接傳回一個json。其實也很簡單,就是在Spring容器中添加一個
ErrorAttributes
對象就可以了,這裡我選擇繼承它的一個子類。
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
//調用父類的方法,會自動擷取内置的那些屬性,如果你不想要,可以不調用這個
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
//添加自定義的屬性
errorAttributes.put("reason","完了,你寫的代碼又産生了一次線上事故");
// 你可以看一下這個方法的參數webRequest這個對象,我相信你肯定能發現好東西
return errorAttributes;
}
}
這就可以了,用兩種請求方式分别測試一個我們的這個自定義屬性是否可用:
2. 使用AOP的異常通知進行處理(推薦)
它的原理就是擷取一個全局的異常通知,然後進行處理。我們隻需要在項目中寫下邊代碼就可以了(其實上邊也隻是寫了一個自定義異常資訊的類)
@ControllerAdvice
public class ErrroAcvice {
/**
* 全局捕獲異常的切面類
* @param request 請求對象,可不傳
* @param response 響應對象,可不傳
* @param e 異常類(這個要和你目前捕獲的異常類是同一個)
*/
@ExceptionHandler(Exception.class) //也可以隻對一個類進行捕獲
public void errorHandler(HttpServletRequest request, HttpServletResponse response,Exception e){
/*
* You can do everything you want to do
* 這裡你拿到了request和response對象,你可以做任何你想做的事
* 比如:
* 1.用request從頭資訊中拿到Accept來判斷是請求方可接收的類型進而進行第一個方法的判斷
* 2.如果你也想傳回一個頁面,使用response對象進行重定向到自己的錯誤頁面就可以了
* 3.你甚至還拿到了異常對象
*/
String accept = request.getHeader("Accept");
// 根據這個字元串來判斷做出什麼響應
try {
response.setStatus(500);
response.getWriter().write("hello");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
3. 兩種方法對比:
- 第一種方法,就是在目前項目中放置一些錯誤狀态碼的頁面讓SpringBoot去查找。也支援自定義傳回的錯誤資訊
- 第二種方法,就是直接使用AOP的思想,進行異常通知處理,自由性很大。
- 我個人建議使用第二種方法,因為自由度很高,可以根據自己的業務邏輯進行随時改變,而且還有一個很大的用處。下一篇文章會有個很好的例子
- 使用了第二種方式後,通過第一種方式放置的錯誤頁面和自定義錯誤資訊全部失效