<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot中的全局異常處理

          共 9133字,需瀏覽 19分鐘

           ·

          2020-11-08 21:18

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質(zhì)文章,第一時間送達

          ? 作者?|??天喬巴夏丶

          來源 |? urlify.cn/yIRB7v

          66套java從入門到精通實戰(zhàn)課程分享

          本篇要點

          • 介紹SpringBoot默認的異常處理機制。

          • 如何定義錯誤頁面。

          • 如何自定義異常數(shù)據(jù)。

          • 如何自定義視圖解析。

          • 介紹@ControllerAdvice注解處理異常。

          一、SpringBoot默認的異常處理機制

          默認情況下,SpringBoot為以下兩種情況提供了不同的響應(yīng)方式:

          1. Browser Clients瀏覽器客戶端:通常情況下請求頭中的Accept會包含text/html,如果未定義/error的請求處理,就會出現(xiàn)如下html頁面:Whitelabel Error Page,關(guān)于error頁面的定制,接下來會詳細介紹。

          1. Machine Clients機器客戶端:Ajax請求,返回ResponseEntity實體json字符串信息。

          {
          ????"timestamp":?"2020-10-30T15:01:17.353+00:00",
          ????"status":?500,
          ????"error":?"Internal?Server?Error",
          ????"trace":?"java.lang.ArithmeticException:?/?by?zero...",
          ????"message":?"/?by?zero",
          ????"path":?"/"
          }

          SpringBoot默認提供了程序出錯的結(jié)果映射路徑/error,這個請求的處理邏輯在BasicErrorController中處理,處理邏輯如下:

          //?判斷mediaType類型是否為text/html
          @RequestMapping(produces?=?MediaType.TEXT_HTML_VALUE)
          public?ModelAndView?errorHtml(HttpServletRequest?request,?HttpServletResponse?response)?{
          ????HttpStatus?status?=?getStatus(request);
          ????Map?model?=?Collections
          ????????.unmodifiableMap(getErrorAttributes(request,?getErrorAttributeOptions(request,?MediaType.TEXT_HTML)));
          ????response.setStatus(status.value());
          ????//?創(chuàng)建ModelAndView對象,返回頁面
          ????ModelAndView?modelAndView?=?resolveErrorView(request,?response,?status,?model);
          ????return?(modelAndView?!=?null)???modelAndView?:?new?ModelAndView("error",?model);
          }

          @RequestMapping
          public?ResponseEntity>?error(HttpServletRequest?request)?{
          ????HttpStatus?status?=?getStatus(request);
          ????if?(status?==?HttpStatus.NO_CONTENT)?{
          ????????return?new?ResponseEntity<>(status);
          ????}
          ????Map?body?=?getErrorAttributes(request,?getErrorAttributeOptions(request,?MediaType.ALL));
          ????return?new?ResponseEntity<>(body,?status);
          }

          二、錯誤頁面的定制

          相信Whitelabel Error Pag頁面我們經(jīng)常會遇到,這樣體驗不是很好,在SpringBoot中可以嘗試定制錯誤頁面,定制方式主要分靜態(tài)和動態(tài)兩種:

          1. 靜態(tài)異常頁面

          classpath:/public/errorclasspath:/static/error路徑下定義相關(guān)頁面:文件名應(yīng)為確切的狀態(tài)代碼,如404.html,或系列掩碼,如4xx.html。

          舉個例子,如果你想匹配404或5開頭的所有狀態(tài)代碼到靜態(tài)的html文件,你的文件目錄應(yīng)該是下面這個樣子:

          src/
          ?+-?main/
          ?????+-?java/
          ?????|???+?<source?code>
          ?????+-?resources/
          ?????????+-?public/
          ?????????????+-?error/
          ?????????????|???+-?404.html
          ???????|???+-?5xx.html
          ?????????????+-?

          如果500.html和5xx.html同時生效,那么優(yōu)先展示500.html頁面。

          1. 使用模板的動態(tài)頁面

          放置在classpath:/templates/error路徑下:這里使用thymeleaf模板舉例:

          src/
          ?+-?main/
          ?????+-?java/
          ?????|???+?<source?code>
          ?????+-?resources/
          ?????????+-?templates/
          ?????????????+-?error/
          ?????????????|???+-?5xx.html
          ?????????????+-?

          頁面如下:


          "en"?xmlns:th="http://www.thymeleaf.org">
          ????
          ????????"UTF-8">
          ????????5xx
          ????
          ????
          ????????

          5xx


          ????????"'error?-->'+?${error}">


          ????????"'status?-->'?+?${status}">


          ????????"'timestamp?-->'?+?${timestamp}">


          ????????"'message?-->'?+?${message}">


          ????????"'path?-->'?+${path}">


          ????????"'trace?-->'?+?${trace}">


          ????

          如果靜態(tài)頁面和動態(tài)頁面同時存在且都能匹配,SpringBoot對于錯誤頁面的優(yōu)先展示規(guī)則如下:

          如果發(fā)生了500錯誤:

          動態(tài)500 -> 靜態(tài)500 -> 動態(tài)5xx -> 靜態(tài)5xx

          三、自定義異常數(shù)據(jù)

          默認的數(shù)據(jù)主要是以下幾個,這些數(shù)據(jù)定義在org.springframework.boot.web.servlet.error.DefaultErrorAttributes中,數(shù)據(jù)的定義在getErrorAttributes方法中。

          DefaultErrorAttributes類在org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration自動配置類中定義:

          ?@Bean
          ?@ConditionalOnMissingBean(value?=?ErrorAttributes.class,
          ??????????????????????????????search?=?SearchStrategy.CURRENT)
          ?public?DefaultErrorAttributes?errorAttributes()?{
          ??return?new?DefaultErrorAttributes();
          ?}

          如果我們沒有提供ErrorAttributes的實例,SpringBoot默認提供一個DefaultErrorAttributes實例。

          因此,我們就該知道如何去自定義異常數(shù)據(jù)屬性:

          1. 實現(xiàn)ErrorAttributes接口。

          2. 繼承DefaultErrorAttributes,本身已經(jīng)定義對異常數(shù)據(jù)的處理,繼承更具效率。

          定義方式如下:

          /**
          ?*?自定義異常數(shù)據(jù)
          ?*?@author?Summerday
          ?*/
          @Slf4j
          @Component
          public?class?MyErrorAttributes?extends?DefaultErrorAttributes?{

          ????@Override
          ????public?Map?getErrorAttributes(WebRequest?webRequest,?ErrorAttributeOptions?options)?{
          ????????Map?map?=?super.getErrorAttributes(webRequest,?options);
          ????????if(map.get("status").equals(500)){
          ????????????log.warn("服務(wù)器內(nèi)部異常");
          ????????}
          ????????return?map;
          ????}
          }

          四、自定義異常視圖

          自定義視圖的加載邏輯存在于BasicErrorController類的errorHtml方法中,用于返回一個ModelAndView對象,這個方法中,首先通過getErrorAttributes獲取到異常數(shù)據(jù),然后調(diào)用resolveErrorView去創(chuàng)建一個ModelAndView對象,只有創(chuàng)建失敗的時候,用戶才會看到默認的錯誤提示頁面。

          @RequestMapping(produces?=?MediaType.TEXT_HTML_VALUE)
          public?ModelAndView?errorHtml(HttpServletRequest?request,?HttpServletResponse?response)?{
          ????HttpStatus?status?=?getStatus(request);
          ????Map?model?=?Collections
          ????????.unmodifiableMap(
          ????????//ErrorAttributes?#?getErrorAttributes
          ????????getErrorAttributes(request,?
          ???????????????????????????getErrorAttributeOptions(request,?MediaType.TEXT_HTML)));
          ????response.setStatus(status.value());
          ????//?E
          ????ModelAndView?modelAndView?=?resolveErrorView(request,?response,?status,?model);
          ????return?(modelAndView?!=?null)???modelAndView?:?new?ModelAndView("error",?model);
          }

          DefaultErrorViewResolver類的resolveErrorView方法:

          @Override
          public?ModelAndView?resolveErrorView(HttpServletRequest?request,?HttpStatus?status,?Map?model)?{
          ????//?以異常狀態(tài)碼作為視圖名去locations路徑中去查找頁面
          ????ModelAndView?modelAndView?=?resolve(String.valueOf(status.value()),?model);
          ????//?如果找不到,以4xx,5xx?series去查找
          ????if?(modelAndView?==?null?&&?SERIES_VIEWS.containsKey(status.series()))?{
          ????????modelAndView?=?resolve(SERIES_VIEWS.get(status.series()),?model);
          ????}
          ????return?modelAndView;
          }

          那么如何自定義呢?和自定義異常數(shù)據(jù)相同,如果我們定義了一個ErrorViewResolver的實例,默認的配置就會失效。

          /**
          ?*?自定義異常視圖解析
          ?*?@author?Summerday
          ?*/

          @Component
          public?class?MyErrorViewResolver?extends?DefaultErrorViewResolver?{

          ????public?MyErrorViewResolver(ApplicationContext?applicationContext,?ResourceProperties?resourceProperties)?{
          ????????super(applicationContext,?resourceProperties);
          ????}

          ????@Override
          ????public?ModelAndView?resolveErrorView(HttpServletRequest?request,?HttpStatus?status,?Map?model)?{
          ????????return?new?ModelAndView("/hyh/resolve",model);
          ????}
          }

          此時,SpringBoot將會去/hyh目錄下尋找resolve.html頁面。

          五、@ControllerAdvice注解處理異常

          前后端分離的年代,后端往往需要向前端返回統(tǒng)一格式的json信息,以下為封裝的AjaxResult對象:

          public?class?AjaxResult?extends?HashMap?{
          ????
          ????//狀態(tài)碼
          ????public?static?final?String?CODE_TAG?=?"code";
          ????
          ????//返回內(nèi)容
          ????public?static?final?String?MSG_TAG?=?"msg";
          ????
          ????//數(shù)據(jù)對象
          ????public?static?final?String?DATA_TAG?=?"data";

          ????private?static?final?long?serialVersionUID?=?1L;

          ????public?AjaxResult()?{
          ????}

          ????public?AjaxResult(int?code,?String?msg)?{
          ????????super.put(CODE_TAG,?code);
          ????????super.put(MSG_TAG,?msg);
          ????}

          ????public?AjaxResult(int?code,?String?msg,?Object?data)?{
          ????????super.put(CODE_TAG,?code);
          ????????super.put(MSG_TAG,?msg);
          ????????if?(data?!=?null)?{
          ????????????super.put(DATA_TAG,?data);
          ????????}
          ????}

          ????public?static?AjaxResult?ok()?{
          ????????return?AjaxResult.ok("操作成功");
          ????}

          ????public?static?AjaxResult?ok(Object?data)?{
          ????????return?AjaxResult.ok("操作成功",?data);
          ????}

          ????public?static?AjaxResult?ok(String?msg)?{
          ????????return?AjaxResult.ok(msg,?null);
          ????}

          ????public?static?AjaxResult?ok(String?msg,?Object?data)?{
          ????????return?new?AjaxResult(HttpStatus.OK.value(),?msg,?data);
          ????}

          ????public?static?AjaxResult?error()?{
          ????????return?AjaxResult.error("操作失敗");
          ????}

          ????public?static?AjaxResult?error(String?msg)?{
          ????????return?AjaxResult.error(msg,?null);
          ????}

          ????public?static?AjaxResult?error(String?msg,?Object?data)?{
          ????????return?new?AjaxResult(HttpStatus.INTERNAL_SERVER_ERROR.value(),?msg,?data);
          ????}

          ????public?static?AjaxResult?error(int?code,?String?msg)?{
          ????????return?new?AjaxResult(code,?msg,?null);
          ????}
          }

          根據(jù)業(yè)務(wù)的需求不同,我們往往也需要自定義異常類,便于維護:

          /**
          ?*?自定義異常
          ?*
          ?*?@author?Summerday
          ?*/
          public?class?CustomException?extends?RuntimeException?{

          ????private?static?final?long?serialVersionUID?=?1L;

          ????private?Integer?code;

          ????private?final?String?message;

          ????public?CustomException(String?message)?{
          ????????this.message?=?message;
          ????}

          ????public?CustomException(String?message,?Integer?code)?{
          ????????this.message?=?message;
          ????????this.code?=?code;
          ????}

          ????public?CustomException(String?message,?Throwable?e)?{
          ????????super(message,?e);
          ????????this.message?=?message;
          ????}

          ????@Override
          ????public?String?getMessage()?{
          ????????return?message;
          ????}

          ????public?Integer?getCode()?{
          ????????return?code;
          ????}
          }

          @ControllerAdvice和@RestControllerAdvice這倆注解的功能之一,就是做到Controller層面的異常處理,而兩者的區(qū)別,與@Controller和@RestController差不多。

          @ExceptionHandler指定需要處理的異常類,針對自定義異常,如果是ajax請求,返回json信息,如果是普通web請求,返回ModelAndView對象。

          /**
          ?*?全局異常處理器
          ?*?@author?Summerday
          ?*/

          @RestControllerAdvice
          public?class?GlobalExceptionHandler?{

          ????private?static?final?Logger?log?=?
          ????????LoggerFactory.getLogger(GlobalExceptionHandler.class);


          ????@ExceptionHandler(CustomException.class)
          ????public?Object?handle(HttpServletRequest?request,?CustomException?e)?{
          ????????AjaxResult?info?=?AjaxResult.error(e.getMessage());
          ????????log.error(e.getMessage());
          ????????//?判斷是否為ajax請求
          ????????if?(isAjaxRequest(request))?{
          ????????????return?info;
          ????????}
          ????????ModelAndView?mv?=?new?ModelAndView();
          ????????mv.setViewName("custom");?//?templates/custom.html
          ????????mv.addAllObjects(info);
          ????????mv.addObject("url",?request.getRequestURL());
          ????????return?mv;
          ????}

          ????private?boolean?isAjaxRequest(HttpServletRequest?request)?{
          ????????return?"XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
          ????}
          }

          在Controller層,人為定義拋出異常:

          @RestController
          public?class?TestController?{

          ????@GetMapping("/ajax")
          ????public?AjaxResult?ajax()?{
          ????????double?alpha?=?0.9;
          ????????if?(Math.random()?????????????throw?new?CustomException("自定義異常!");
          ????????}
          ????????return?AjaxResult.ok();
          ????}
          }

          最后,通過/templates/custom.html定義的動態(tài)模板頁面展示數(shù)據(jù):


          "en"?xmlns:th="http://www.thymeleaf.org">
          ????
          ????????"UTF-8">
          ????????自定義界面
          ????
          ????
          ????????"'msg?-->'+?${msg}">


          ????????"'code?-->'+?${code}">


          ????????"'url?-->'+?${url}">


          ????

          源碼下載

          本文內(nèi)容均為對優(yōu)秀博客及官方文檔總結(jié)而得,原文地址均已在文中參考閱讀處標注。最后,文中的代碼樣例已經(jīng)全部上傳至Gitee:https://gitee.com/tqbx/springboot-samples-learn

          參考閱讀

          • Spring Boot干貨系列:(十三)Spring Boot全局異常處理整理

          • Springboot:Error-handling

          • 江南一點雨:Spring Boot 中關(guān)于自定義異常處理的套路!





          粉絲福利:實戰(zhàn)springboot+CAS單點登錄系統(tǒng)視頻教程免費領(lǐng)取

          ???

          ?長按上方微信二維碼?2 秒
          即可獲取資料



          感謝點贊支持下哈?

          瀏覽 41
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本无码色情三级播放免费看电影 | 黄a免费视频在线观看 | 欧美黄色视屏一区二区三区 | 性色av影院 | 韩国三级在线视频 |