1html
錯誤處理有原理分析git
使用SpringBoot建立的web項目中,當咱們請求的頁面不存在(http狀態碼爲404),或者服務器發生異常(http狀態碼通常爲500)時,SpringBoot就會給咱們返回錯誤信息。web
也就是說,在SpringBoot的web項目中,會自動建立一個/error的錯誤接口,來返回錯誤信息。可是針對不一樣的訪問方式,會有如下兩種不一樣的返回信息。這主要取決於你訪問時的http頭部信息的Accept
這個值來指定你能夠接收的類型有哪些json
使用瀏覽器訪問時的頭信息及其返回結果後端
Accept: text/html
使用其餘設備,如手機客戶端等訪問時頭部信息及其返回結果(通常是在先後端分離的架構中)設計模式
Accept: */*
2瀏覽器
進行錯誤處理服務器
處理異常主要有兩種方式:微信
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"}
屬性上
有模板引擎的狀況(能夠用於渲染頁面)
項目中使用的了模板引擎,如:thymeleaf 、freemarker等做爲頁面的渲染時。在templates建立/error文件夾並添加錯誤的狀態碼對應的.html文件,以下圖:
這裏的404和500就是肯定的錯誤狀態碼,而4xx表示其餘的4開頭的錯誤,如400,401等。固然能夠爲每個狀態碼都設置對應的錯誤頁面,可是這樣作,並無什麼好處,因此就直接使用4xx.html這樣的泛指代替了。
能夠在咱們錯誤頁面中獲取到以下信息(就是ModelAndView對象中的內容):
細心的小夥伴會發現,這個其實就是當你用手機請求時返回的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文件夾下就能夠了。
不過此時並不能獲取上邊的那些信息了,由於這本就是靜態資源,沒有模板引擎進行渲染
{
"timestamp": "2020-04-22T16:13:37.506+0000",
"status": 500,
"error": "Internal Server Error",
"message": "/ by zero",
"path": "/hello/haserror",
"reason": "完了,你寫的代碼又產生了一次線上事故"
}
這纔是最重要的內容,由於這個信息不只是作爲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的異常通知進行處理(推薦)
它的原理就是獲取一個全局的異常通知,而後進行處理。咱們只須要在項目中寫下邊代碼就能夠了(其實上邊也只是寫了一個自定義異常信息的類)
若是對AOP有問題的小夥伴能夠在後臺回覆Spring在教程中查看SpringAOP全系列文章
@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();
}
}
}
總結與對比
第一種方法,就是在當前項目中放置一些錯誤狀態碼的頁面讓SpringBoot去查找。也支持自定義返回的錯誤信息
第二種方法,就是直接使用AOP的思想,進行異常通知處理,自由性很大。
我我的建議使用第二種方法,由於自由度很高,能夠根據本身的業務邏輯進行隨時改變,並且還有一個很大的用處。下一篇文章會有個很好的例子
使用了第二種方式後,經過第一種方式放置的錯誤頁面和自定義錯誤信息所有失效
沒代碼,說什麼都是瞎扯
關注公衆號後臺回覆 2000 獲取
2020-04-16
2020-04-14
微服務
設計模式
數據結構
關注我 有好貨
本文分享自微信公衆號 - 小魚與Java(Fish_Java)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。