SpringBoot優(yōu)雅的全局異常處理

前言

在日常項目開發(fā)中,異常是常見的笋妥,但是如何更高效的處理好異常信息,讓我們能快速定位到BUG窄潭,是很重要的春宣,不僅能夠提高我們的開發(fā)效率,還能讓你代碼看上去更舒服嫉你,SpringBoot的項目已經(jīng)有一定的異常處理了月帝,但是對于我們開發(fā)者而言可能就不太合適了,因此我們需要對這些異常進行統(tǒng)一的捕獲并處理幽污。

SpringBoot默認的錯誤處理機制

返回錯誤頁面

默認返回 Whitelabel Error Page頁面的樣式太單調(diào)嚷辅,用戶體驗不好。

image.png

如 果 我 們 需 要 將 所 有 的 異 常 同 一 跳 轉(zhuǎn) 到 自 定 義 的 錯 誤 頁 面 距误, 需 要 再 src/main/resources/templates 目錄下創(chuàng)建 error.html 頁面潦蝇。

注意:名稱必須叫 error

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
        <!--SpringBoot默認存儲異常信息的key為exception-->
    <span th:text="${exception}" />
</body>
</html>

返回json格式api

Json格式的結(jié)果字符串不統(tǒng)一,與前端人員約定統(tǒng)一格式不一致


image.png

源碼分析

SpringBoot在頁面 發(fā)生異常的時候會自動把請求轉(zhuǎn)到/error深寥,SpringBoot內(nèi)置了一個BasicErrorController對異常進行統(tǒng)一的處理,當然也可以自定義這個路徑

image.png
@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.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

    @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.getErrorAttributeOptions(request, MediaType.ALL));
            return new ResponseEntity(body, status);
        }
    }

我們可以看到剛好對照兩個方法一個返回錯誤頁面贤牛,一個返回錯誤字符惋鹅,默認錯誤路徑是/error如果有自定義就用自定義的

server.error.path=/custom/error

自定義錯誤處理

SpringBoot提供了ErrorAttribute類型
自定義ErrorAttribute類型的bean還是默認的兩種響應方式,只不過改變了響應內(nèi)容項而已

package cn.soboys.core;


import cn.hutool.core.bean.BeanUtil;
import cn.soboys.core.ret.Result;
import cn.soboys.core.ret.ResultCode;
import cn.soboys.core.ret.ResultResponse;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/18 14:14
 * 全局錯誤
 */
@Component
public class GlobalErrorHandler extends DefaultErrorAttributes {


    /**
     * 自定義錯誤返回頁面
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @return
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        return super.resolveException(request, response, handler, ex);
    }

    /**
     * 自定義錯誤返回格式
     *
     * @param webRequest
     * @param options
     * @return
     */
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
        Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
        Result result = ResultResponse.failure(ResultCode.NOT_FOUND, errorAttributes.get("path"));
        Map map = BeanUtil.beanToMap(result, true, true);
        return map;
    }
}

自定義業(yè)務異常類

繼承RuntimeException

package cn.soboys.core.authentication;

import cn.soboys.core.ret.ResultCode;
import lombok.Data;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/22 13:58
 * 認證異常
 */
@Data
public class AuthenticationException extends RuntimeException {

    public AuthenticationException(String message) {
        super(message);
    }

}

全局捕獲異常

通過SpringBoot提供的@RestControllerAdvice@ControllerAdvice 結(jié)合@ExceptionHandler使用

@RestControllerAdvice@ControllerAdvice區(qū)別和@RestController殉簸,@Controller一樣如果想返回json格式也可以單獨使用@ResponseBody注解在方法上

需要捕獲什么異常通過@ExceptionHandler來指定對應異常類就可以了這里原則是按照從小到大異常進行依次執(zhí)行

通俗來講就是當小的異常沒有指定捕獲時闰集,大的異常包含了此異常就會被執(zhí)行比如Exception 異常包含了所有異常類,是所有異常超級父類般卑,當出現(xiàn)沒有指定異常時此時對應捕獲了Exception異常的方法會執(zhí)行

@ExceptionHandler注解處理異常

@Controller
public class DemoController {
    @RequestMapping("/show")
    public String showInfo() {
        String str = null;
        str.length();
        return "index";
    }

    @RequestMapping("/show2")
    public String showInfo2() {
        int a = 10 / 0;
        return "index";
    }

    /**
     * java.lang.ArithmeticException 該方法需要返回一個 ModelAndView:目的是可以讓我們封裝異常信息以及視
     * 圖的指定 參數(shù) Exception e:會將產(chǎn)生異常對象注入到方法中
     */
    @ExceptionHandler(value = { java.lang.ArithmeticException.class })
    public ModelAndView arithmeticExceptionHandler(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("error", e.toString());
        mv.setViewName("error1");
        return mv;
    }

    /**
     * java.lang.NullPointerException 該方法需要返回一個 ModelAndView:目的是可以讓我們封裝異常信息以及視
     * 圖的指定 參數(shù) Exception e:會將產(chǎn)生異常對象注入到方法中
     */
    @ExceptionHandler(value = { java.lang.NullPointerException.class })
    public ModelAndView nullPointerExceptionHandler(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("error", e.toString());
        mv.setViewName("error2");
        return mv;
    }
}

優(yōu)點:可以自定義異常信息存儲的key,自定義跳轉(zhuǎn)視圖的名稱

缺點:需要編寫大量的異常處理方法,不能跨controller武鲁,如果兩個controller中出現(xiàn)同樣的異常,需要重新編寫異常處理的方法

@ControllerAdvice+@ExceptionHandler 注解處理異常

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/17 20:19
 * 全局異常統(tǒng)一處理
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
 /**
     * 認證異常
     * @param e
     * @return
     */
    @ExceptionHandler(AuthenticationException.class)
    public Result UnNoException(AuthenticationException e) {
        return ResultResponse.failure(ResultCode.UNAUTHORIZED,e.getMessage());
    }

    /**
     *
     * @param e 未知異常捕獲
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Result UnNoException(Exception e) {
        return ResultResponse.failure(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage());
    }
}

優(yōu)點:可以自定義異常信息存儲的key,自定義跳轉(zhuǎn)視圖的名稱,跨controller統(tǒng)一攔截統(tǒng)一捕獲蝠检,一般都是使用這種

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沐鼠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子叹谁,更是在濱河造成了極大的恐慌饲梭,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焰檩,死亡現(xiàn)場離奇詭異憔涉,居然都是意外死亡,警方通過查閱死者的電腦和手機析苫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門兜叨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穿扳,“玉大人,你說我怎么就攤上這事国旷∶铮” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵议街,是天一觀的道長泽谨。 經(jīng)常有香客問我,道長特漩,這世上最難降的妖魔是什么吧雹? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮涂身,結(jié)果婚禮上雄卷,老公的妹妹穿的比我還像新娘。我一直安慰自己蛤售,他們只是感情好丁鹉,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著悴能,像睡著了一般揣钦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上漠酿,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天冯凹,我揣著相機與錄音,去河邊找鬼炒嘲。 笑死宇姚,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的夫凸。 我是一名探鬼主播浑劳,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼夭拌!你這毒婦竟也來了魔熏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鸽扁,失蹤者是張志新(化名)和其女友劉穎道逗,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體献烦,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡滓窍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了巩那。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吏夯。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡此蜈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出噪生,到底是詐尸還是另有隱情裆赵,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布跺嗽,位于F島的核電站战授,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏桨嫁。R本人自食惡果不足惜植兰,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望璃吧。 院中可真熱鬧楣导,春花似錦、人聲如沸畜挨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巴元。三九已至毡咏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逮刨,已是汗流浹背血当。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留禀忆,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓落恼,卻偏偏與公主長得像箩退,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子佳谦,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容