Springboot錯(cuò)誤處理機(jī)制實(shí)現(xiàn)原理解析
1.默認(rèn)的錯(cuò)誤機(jī)制
默認(rèn)效果
①在瀏覽器中訪問(wèn)不存在的請(qǐng)求時(shí),springboot默認(rèn)返回一個(gè)空白頁(yè)面
瀏覽器的請(qǐng)求頭
②客戶端訪問(wèn)時(shí),返回json數(shù)據(jù)
{ 'timestamp': '2020-03-24T02:49:56.572+0000', 'status': 404, 'error': 'Not Found', 'message': 'No message available', 'path': '/'}
客戶端訪問(wèn)的請(qǐng)求頭
原理
可以參照 ErrorMvcAutoConfiguration 錯(cuò)誤處理的自動(dòng)配置
給容器中添加了以下組件
1.DefaultErrorAttributes
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap(); errorAttributes.put('timestamp', new Date()); this.addStatus(errorAttributes, webRequest); this.addErrorDetails(errorAttributes, webRequest, includeStackTrace); this.addPath(errorAttributes, webRequest); return errorAttributes;}
@RequestMapping( produces = {'text/html'} ) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = this.getStatus(request); //處理頁(yè)面的請(qǐng)求返回給前臺(tái)數(shù)據(jù) model 的獲取 ,調(diào)用 Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); ModelAndView modelAndView = this.resolveErrorView(request, response, status, model); return modelAndView != null ? modelAndView : new ModelAndView('error', model); } //調(diào)用 AbstractErrorController#getErrorAttributes protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) { WebRequest webRequest = new ServletWebRequest(request); return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace); } 最終調(diào)用DefaultErrorAttributes#getErrorAttributes public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
2.BasicErrorController : 處理默認(rèn)的 /error 請(qǐng)求
@Controller@RequestMapping({'${server.error.path:${error.path:/error}}'}) public class BasicErrorController extends AbstractErrorController { private final ErrorProperties errorProperties;public String getErrorPath() { return this.errorProperties.getPath();}@RequestMapping( produces = {'text/html'} //產(chǎn)生html類型的數(shù)據(jù),瀏覽器發(fā)送的請(qǐng)求來(lái)到這個(gè)方法處理)public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {//獲取狀態(tài)碼 HttpStatus status = this.getStatus(request);//獲取模型數(shù)據(jù) Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value());//去哪個(gè)頁(yè)面作為錯(cuò)誤頁(yè)面,包括頁(yè)面地址和內(nèi)容 ModelAndView modelAndView = this.resolveErrorView(request, response, status, model); return modelAndView != null ? modelAndView : new ModelAndView('error', model);}@RequestMapping //產(chǎn)生json類型的數(shù)據(jù), 其他客戶端發(fā)送的請(qǐng)求來(lái)到這個(gè)方法處理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); }}
3.ErrorPageCustomizer
public class ErrorProperties { @Value('${error.path:/error}') private String path = '/error'; //系統(tǒng)出現(xiàn)錯(cuò)誤請(qǐng)求之后來(lái)到 /error 請(qǐng)求進(jìn)行處理 ,(類似于以前 web.xml 中注冊(cè)的錯(cuò)誤頁(yè)面規(guī)則)
4.DefaultErrorViewResolver
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) { ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) {//默認(rèn) springboot 可以找到這個(gè)頁(yè)面 error/404 String errorViewName = 'error/' + viewName;//模板引擎可以解析這個(gè)頁(yè)面地址就用模板引擎解析 TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);//模板引擎可用的情況下就返回到 errorViewName 指定的視圖地址 return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model); } //模板引擎不可用就在靜態(tài)資源文件夾里面找 errorViewName 對(duì)應(yīng)的頁(yè)面 error/404.html private ModelAndView resolveResource(String viewName, Map<String, Object> model) { String[] var3 = this.resourceProperties.getStaticLocations(); int var4 = var3.length; for(int var5 = 0; var5 < var4; ++var5) { String location = var3[var5]; try {Resource resource = this.applicationContext.getResource(location);resource = resource.createRelative(viewName + '.html');//如果靜態(tài)資源文件中由 這個(gè)資源就直接使用,否則返回為空if (resource.exists()) { return new ModelAndView(new DefaultErrorViewResolver.HtmlResourceView(resource), model);} } catch (Exception var8) { } } return null; }
步驟:
一旦系統(tǒng)出現(xiàn) 4xx 或者 5xx 之類的錯(cuò)誤,ErrorPageCustomizer 就會(huì)生效(定制錯(cuò)誤的響應(yīng)規(guī)則),就會(huì)來(lái)到 /error 請(qǐng)求,會(huì)被BasicErrorController
處理。
①響應(yīng)頁(yè)面 去哪個(gè)頁(yè)面由 DefaultErrorViewResolver 決定
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) { Iterator var5 = this.errorViewResolvers.iterator(); //解析所有的 ErrorViewResolver 得到 modelAndView ModelAndView modelAndView; do { if (!var5.hasNext()) { return null; } ErrorViewResolver resolver = (ErrorViewResolver)var5.next(); modelAndView = resolver.resolveErrorView(request, status, model); } while(modelAndView == null); return modelAndView;}
2.錯(cuò)誤信息的定制
①如何定制錯(cuò)誤頁(yè)面
1>有模板引擎的情況下: error/狀態(tài)碼 ;【將錯(cuò)誤頁(yè)面命名為 錯(cuò)誤碼.html 放在模板引擎文件夾下的 error 文件夾下】,發(fā)生此狀態(tài)碼的錯(cuò)誤就來(lái)到
對(duì)應(yīng)的頁(yè)面;
我們可以使用 4xx 和 5xx 作為錯(cuò)誤頁(yè)面的文件名來(lái)匹配這種類型的所欲錯(cuò)誤,精確優(yōu)先(優(yōu)先尋找精確的 狀態(tài)碼.html );
頁(yè)面能夠獲取到的信息
timestamp :時(shí)間戳
status : 狀態(tài)碼
exception : 異常對(duì)象
message : 異常消息
errors : JSR303數(shù)據(jù)校驗(yàn)的錯(cuò)誤都在這兒
2>.沒(méi)有模板引擎(模板引擎找不到這個(gè)頁(yè)面),靜態(tài)資源文件夾下找
3>.以上都沒(méi)有錯(cuò)誤頁(yè)面,就默認(rèn)來(lái)到 springboot 默認(rèn)的錯(cuò)誤頁(yè)面
②、自定義異常處理&返回定制json數(shù)據(jù);
@ControllerAdvicepublic class MyExceptionHandler { @ResponseBody @ExceptionHandler(UserNotExistException.class) public Map<String,Object> handleException(Exception e){ Map<String,Object> map = new HashMap<>(); map.put('code','user.notexist'); map.put('message',e.getMessage()); return map; }}//通過(guò)異常處理器,但沒(méi)有自適應(yīng)效果(瀏覽器返回頁(yè)面,客戶端訪問(wèn)返回json數(shù)據(jù))
2)、轉(zhuǎn)發(fā)到/error進(jìn)行自適應(yīng)響應(yīng)效果處理
@RequestMapping( produces = {'text/html'})public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { //獲取錯(cuò)誤的狀態(tài)碼,在分析的過(guò)程中,要注意參數(shù)從哪兒來(lái)? =======》前領(lǐng)導(dǎo)的一句話,哈哈…… HttpStatus status = this.getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value());//依據(jù)錯(cuò)誤狀態(tài)碼解析錯(cuò)誤試圖,如果直接轉(zhuǎn)發(fā),不指定錯(cuò)誤狀態(tài)碼則試圖解析出錯(cuò)(直接轉(zhuǎn)發(fā)狀態(tài)碼為 200 ,到不了定制的 4xx 5xx 的頁(yè)面) ModelAndView modelAndView = this.resolveErrorView(request, response, status, model); return modelAndView != null ? modelAndView : new ModelAndView('error', model);}
@ExceptionHandler(UserNotExistException.class) public String handleException(Exception e, HttpServletRequest request){ Map<String,Object> map = new HashMap<>(); <strong>//傳入我們自己的錯(cuò)誤狀態(tài)碼 4xx 5xx,否則就不會(huì)進(jìn)入定制錯(cuò)誤頁(yè)面的解析流程</strong> /** * Integer statusCode = (Integer) request .getAttribute('javax.servlet.error.status_code'); */ request.setAttribute('javax.servlet.error.status_code',500); map.put('code','user.notexist'); map.put('message',e.getMessage()); //轉(zhuǎn)發(fā)到/error return 'forward:/error'; }
3)、將我們的定制數(shù)據(jù)攜帶出去;======》即修改model中的值即可
出現(xiàn)錯(cuò)誤以后,會(huì)來(lái)到/error請(qǐng)求,會(huì)被BasicErrorController處理,響應(yīng)出去可以獲取的數(shù)據(jù)是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)規(guī)定的方法);
1、完全來(lái)編寫一個(gè)ErrorController的實(shí)現(xiàn)類【或者是編寫AbstractErrorController的子類】,放在容器中;
2、頁(yè)面上能用的數(shù)據(jù),或者是json返回能用的數(shù)據(jù)都是通過(guò)errorAttributes.getErrorAttributes得到;
容器中DefaultErrorAttributes.getErrorAttributes();默認(rèn)進(jìn)行數(shù)據(jù)處理的;
自定義ErrorAttributes
//給容器中加入我們自己定義的ErrorAttributes@Componentpublic class MyErrorAttributes extends DefaultErrorAttributes { @Override public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace); map.put('company','atguigu'); return map; }}
最終的效果:響應(yīng)是自適應(yīng)的,可以通過(guò)定制ErrorAttributes改變需要返回的內(nèi)容,
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。
相關(guān)文章:
1. phpstudy apache開啟ssi使用詳解2. vue3+ts+elementPLus實(shí)現(xiàn)v-preview指令3. Xml簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理4. JSP之表單提交get和post的區(qū)別詳解及實(shí)例5. 詳解瀏覽器的緩存機(jī)制6. xml中的空格之完全解說(shuō)7. jsp實(shí)現(xiàn)登錄驗(yàn)證的過(guò)濾器8. jsp文件下載功能實(shí)現(xiàn)代碼9. 使用Hangfire+.NET 6實(shí)現(xiàn)定時(shí)任務(wù)管理(推薦)10. 如何在jsp界面中插入圖片
