前言
在日常項目開發(fā)中,異常是常見的笋妥,但是如何更高效的處理好異常信息,讓我們能快速定位到BUG窄潭,是很重要的春宣,不僅能夠提高我們的開發(fā)效率,還能讓你代碼看上去更舒服嫉你,SpringBoot的項目已經(jīng)有一定的異常處理了月帝,但是對于我們開發(fā)者而言可能就不太合適了,因此我們需要對這些異常進行統(tǒng)一的捕獲并處理幽污。
SpringBoot默認的錯誤處理機制
返回錯誤頁面
默認返回 Whitelabel Error Page頁面的樣式太單調(diào)嚷辅,用戶體驗不好。
如 果 我 們 需 要 將 所 有 的 異 常 同 一 跳 轉(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)一格式不一致
源碼分析
SpringBoot在頁面 發(fā)生異常的時候會自動把請求轉(zhuǎn)到/error深寥,SpringBoot內(nèi)置了一個BasicErrorController對異常進行統(tǒng)一的處理,當然也可以自定義這個路徑
@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)一捕獲蝠检,一般都是使用這種