javascript
Spring Boot错误处理机制以及定制自己的错误页面
在我們使用Spring Boot的過程中肯定有遇到過發(fā)生錯(cuò)誤的時(shí)候,這個(gè)時(shí)候可能頁面上出現(xiàn)的就是Spring Boot默認(rèn)的錯(cuò)誤界面,有的時(shí)候我們希望能顯示我們自己設(shè)置的錯(cuò)誤界面,或者攜帶我們自己的錯(cuò)誤響應(yīng)參數(shù),比如查詢用戶是否存在的時(shí)候,如果用戶不存在,控制器可以選擇拋出一個(gè)用戶不存在異常,這個(gè)時(shí)候我們希望錯(cuò)誤響應(yīng)參數(shù)可以攜帶一個(gè)message響應(yīng)參數(shù)表示用戶不存在,而不是Spring Boot為我們配置好的那幾個(gè)參數(shù),這個(gè)時(shí)候我們應(yīng)該這么做呢,這就是本文的目的。
如果要配置自己的錯(cuò)誤界面,首先我們需要先了解一下Spring Boot是如何處理錯(cuò)誤的。Spring Boot在自動(dòng)配置類ErrorMvcAutoConfiguration中為我們添加了4個(gè)錯(cuò)誤處理相關(guān)的組件,具體可以在IDEA中使用Ctrl+N來查找這個(gè)類,他們的作用可以查看下面代碼中的注釋:
1、DefaultErrorAttributes
//幫我們設(shè)置錯(cuò)誤響應(yīng)參數(shù); @Override public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,boolean includeStackTrace) {Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();errorAttributes.put("timestamp", new Date());addStatus(errorAttributes, requestAttributes);addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);addPath(errorAttributes, requestAttributes);return errorAttributes; }2、BasicErrorController
//處理/error請求,根據(jù)請求頭中的信息(Accept參數(shù))來判斷優(yōu)先返回html頁面,還是JSON數(shù)據(jù) @Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class BasicErrorController extends AbstractErrorController {@RequestMapping(produces = "text/html")//產(chǎn)生html類型的數(shù)據(jù);瀏覽器發(fā)送的請求來到這個(gè)方法處理public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {HttpStatus status = getStatus(request);Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));response.setStatus(status.value());//去哪個(gè)頁面作為錯(cuò)誤頁面;包含頁面地址和頁面內(nèi)容ModelAndView modelAndView = resolveErrorView(request, response, status, model);return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);}@RequestMapping@ResponseBody //產(chǎn)生json數(shù)據(jù),其他客戶端來到這個(gè)方法處理;public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {Map<String, Object> body = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.ALL));HttpStatus status = getStatus(request);return new ResponseEntity<Map<String, Object>>(body, status);} }3、ErrorPageCustomizer
/** 配置錯(cuò)誤發(fā)生后,到哪去。這里是系統(tǒng)出現(xiàn)錯(cuò)誤以后來到error請求進(jìn)行處理; 也就是到上面的BasicErrorController處理器,(相當(dāng)于以前web.xml注冊的錯(cuò)誤頁面規(guī)則)。 可以看到這個(gè)參數(shù)是可配置的。 **/ @Value("${error.path:/error}") private String path = "/error";4、DefaultErrorViewResolver
@Override public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,Map<String, Object> model) {ModelAndView modelAndView = resolve(String.valueOf(status), model);if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);}return modelAndView; }private ModelAndView resolve(String viewName, Map<String, Object> model) {//默認(rèn)SpringBoot可以去找到一個(gè)頁面? 比如error/404String errorViewName = "error/" + viewName;//模板引擎可以解析這個(gè)頁面地址就用模板引擎解析TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);if (provider != null) {//模板引擎可用的情況下返回到errorViewName指定的視圖地址return new ModelAndView(errorViewName, model);}//模板引擎不可用,就在靜態(tài)資源文件夾下找errorViewName對應(yīng)的頁面error/404.htmlreturn resolveResource(errorViewName, model); }錯(cuò)誤響應(yīng)步驟如下:
一但系統(tǒng)出現(xiàn)4xx或者5xx之類的錯(cuò)誤;ErrorPageCustomizer就會(huì)生效(定制錯(cuò)誤的響應(yīng)規(guī)則);就會(huì)來到/error請求;就會(huì)被BasicErrorController處理;如果決定產(chǎn)生html數(shù)據(jù),而不是直接返回JSON數(shù)據(jù),就使用DefaultErrorViewResolver試圖解析器來解析我們的ModelAndView,最終決定返回哪個(gè)html文件。我們可以使用4xx和5xx作為錯(cuò)誤頁面的文件名來匹配這種類型的所有錯(cuò)誤,精確優(yōu)先(優(yōu)先尋找精確的狀態(tài)碼.html,比如發(fā)生404錯(cuò)誤,有404.html就不去4xx.html了);我們將這些錯(cuò)誤響應(yīng)的.html文件放到靜態(tài)資源路徑下的/error文件夾下即可。
通過上面的描述如果需要返回自己的錯(cuò)誤返回界面,直接在靜態(tài)資源文件夾下新建/error文件夾,將自己的錯(cuò)誤響應(yīng).html文件放到里面即可,這些.html文件可能是4xx.html、404.html、5xx.html、500.html。具體響應(yīng)哪個(gè).html文件根據(jù)錯(cuò)誤代碼來決定。如果需要自定義錯(cuò)誤響應(yīng)信息,那么你還需要像下面這樣做:
1、只響應(yīng)JSON數(shù)據(jù),而不管請求的是什么(比如請求.html頁面,也返回JSON數(shù)據(jù))
首先新建@ControllerAdvice類,而后在其中寫@ExceptionHandler方法即可,如下:
@ControllerAdvice public 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ān)于@ControllerAdvice注解更加詳細(xì)的解釋你可以看這里,在本文章中主要是結(jié)合@ExceptionHandler注解來處理錯(cuò)誤。另外UserNotExistException是我們自定義的一個(gè)錯(cuò)誤,由Controller拋出。
2、自適應(yīng)響應(yīng)
上面處理的方式有一個(gè)問題,就是不管是頁面請求,還是JSON數(shù)據(jù)請求,我們都是返回JSON數(shù)據(jù),如何做到請求JSON數(shù)據(jù)就返回JSON數(shù)據(jù),請求頁面就返回頁面呢?我們可以回到上面說到的BasicErrorController組件,這個(gè)組件會(huì)自適應(yīng)的處理/error請求,所以我們可以轉(zhuǎn)發(fā)到/error界面,如下所示:
@ControllerAdvice public class MyExceptionHandler {@ExceptionHandler(UserNotExistException.class)public String handleException(Exception e, HttpServletRequest request){//傳入我們自己的錯(cuò)誤狀態(tài)碼 4xx 5xx,否則就不會(huì)進(jìn)入定制錯(cuò)誤頁面的解析流程request.setAttribute("javax.servlet.error.status_code",500);//轉(zhuǎn)發(fā)到/errorreturn "forward:/error";} }但是,這個(gè)時(shí)候我們又無法響應(yīng)我們自己定制的錯(cuò)誤響應(yīng)數(shù)據(jù)了,我們希望不但可以自適應(yīng),又可以攜帶我們自定義的數(shù)據(jù)。如果你足夠細(xì)心的話,可以發(fā)現(xiàn),在上面BasicErrorController組件中,不論是響應(yīng)HTML頁面還是反回JSON數(shù)據(jù),都會(huì)調(diào)用getErrorAttributes這個(gè)方法,來設(shè)置錯(cuò)誤響應(yīng)數(shù)據(jù),這個(gè)方法來自容器中的DefaultErrorAttributes組件,這個(gè)組件會(huì)在我們沒有在容器中添加ErrorAttributes組件的前提下添加到容器中作為默認(rèn)的ErrorAttributes以返回錯(cuò)誤響應(yīng)數(shù)據(jù);所以我們?nèi)绻憫?yīng)我們自己的數(shù)據(jù),我們就要寫一個(gè)ErrorAttributes,并且實(shí)現(xiàn)它的getErrorAttributes方法,然后加入到容器中,為了簡單起見我們直接,繼承DefaultErrorAttributes然后重寫getErrorAttributes方法即可:
//給容器中加入我們自己定義的ErrorAttributes @Component public class MyErrorAttributes extends DefaultErrorAttributes {@Overridepublic Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,boolean includeStackTrace) {//保留默認(rèn)的錯(cuò)誤處理數(shù)據(jù)Map<String, Object> map = super.getErrorAttributes(requestAttributes,includeStackTrace);//由具體的錯(cuò)誤處理器填入的數(shù)據(jù),第二個(gè)參數(shù)的意思是scope,也就是從哪個(gè)域中獲取,0:request,1:session, .....map.put("ext", RequestAttributes.getAttribute("ext", 0));//所有錯(cuò)誤都會(huì)填入的錯(cuò)誤響應(yīng)數(shù)據(jù)map.put("company","BaiDu");return map;} }上面注釋中說到的 “由具體的錯(cuò)誤處理器填入的數(shù)據(jù)” 的意思是如下:
@ControllerAdvice public class MyExceptionHandler {@ExceptionHandler(UserNotExistException.class)public String handleException(Exception e, HttpServletRequest request){//傳入我們自己的錯(cuò)誤狀態(tài)碼 4xx 5xx,否則就不會(huì)進(jìn)入定制錯(cuò)誤頁面的解析流程request.setAttribute("javax.servlet.error.status_code",500);//填入額外的錯(cuò)誤響應(yīng)處理數(shù)據(jù):Map<String,Object> map = new HashMap<>();map.put("code","user.notexist");map.put("message",e.getMessage());request.setAttribute("ext", map);//轉(zhuǎn)發(fā)到/errorreturn "forward:/error";} }這樣一來我們就不但可以自適應(yīng)響應(yīng)數(shù)據(jù),還可以定制自己的錯(cuò)誤響應(yīng)信息了。
總結(jié):
上面詳細(xì)講述了Spring Boot中發(fā)生錯(cuò)誤后,如何處理的的流程,并且自定義錯(cuò)誤響應(yīng)數(shù)據(jù)和界面。讀者可以選擇自己需要的功能來選擇如何處理,主要是懂得原理,這些處理方式就能使用的游刃有余。簡單分為以下兩點(diǎn):
- 不需要自定義錯(cuò)誤響應(yīng)數(shù)據(jù),只要到我們自己的錯(cuò)誤響應(yīng)界面,則編寫錯(cuò)誤代碼對應(yīng)的HTML文件,并將這些錯(cuò)誤響應(yīng)的.html文件放到靜態(tài)資源路徑下的/error文件夾下即可。
- 需要自定義錯(cuò)誤響應(yīng)數(shù)據(jù),并且自適應(yīng)響應(yīng),則在容器中加入我們自己的ErrorAttributes組件即可。
總結(jié)
以上是生活随笔為你收集整理的Spring Boot错误处理机制以及定制自己的错误页面的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Boot注册Servlet
- 下一篇: Spring Boot数据库操作原理及整