SpringBoot優(yōu)雅地實(shí)現(xiàn)全局異常處理的方法詳解
前言
在前一節(jié)的學(xué)習(xí)中,慕歌帶大家使用了全局結(jié)果集返回,通過使用全局結(jié)果集配置,優(yōu)雅的返回后端數(shù)據(jù),為前端的數(shù)據(jù)拿取提供了非常好的參考。同時(shí)通過不同的狀態(tài)碼返回,我們能夠清晰的了解報(bào)錯(cuò)的位置,排除錯(cuò)誤。如果大家有需要,可以使用我提供的的同一結(jié)果集以及狀態(tài)碼,并且可以使用全局異常攔截,實(shí)現(xiàn)異常的標(biāo)準(zhǔn)返回。接下來,我們一起來了解如何使用全局異常處理吧!
異常工具
先定義一個(gè)合適 的異常處理類,在之后的異常都會(huì)以這種格式返回前端,前端根據(jù)我們的異常進(jìn)行自己的返回,以一種優(yōu)雅的方式呈現(xiàn)錯(cuò)誤,優(yōu)化用戶體驗(yàn)。
異常結(jié)果集:
/**
* 返回結(jié)果封裝
*/
@Data
public class ResultVo {
// 狀態(tài)碼
private int code;
// 狀態(tài)信息
private String msg;
// 返回對(duì)象
private Object data;
// 手動(dòng)設(shè)置返回vo
public ResultVo(int code, String msg) {
this.code = code;
this.msg = msg;
}
// 手動(dòng)設(shè)置返回vo
public ResultVo(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
// 只返回狀態(tài)碼
public ResultVo(StatusCode statusCode) {
this.code = statusCode.getCode();
this.msg = statusCode.getMsg();
}
// 默認(rèn)返回成功狀態(tài)碼,數(shù)據(jù)對(duì)象
public ResultVo(Object data) {
this.code = ResultCode.SUCCESS.getCode();
this.msg = ResultCode.SUCCESS.getMsg();
this.data = data;
}
// 返回指定狀態(tài)碼,數(shù)據(jù)對(duì)象
public ResultVo(StatusCode statusCode, Object data) {
this.code = statusCode.getCode();
this.msg = statusCode.getMsg();
this.data = data;
}
public ResultVo(StatusCode statusCode,String msg, Object data) {
this.code = statusCode.getCode();
this.msg = msg;
this.data = data;
}
}
異常狀態(tài)碼,通過返回的狀態(tài)碼,以及狀態(tài)信息,能夠高效反映錯(cuò)誤,并且可以全局統(tǒng)一管理,方便快捷:
@Getter
public enum ExceptionCode implements StatusCode {
// 系統(tǒng)級(jí)別錯(cuò)誤碼
ERROR(-1, "操作異常"),
NOT_LOGIN(102, "請(qǐng)先登錄!"),
NO_Role(102,"無權(quán)限"),
NO_PERMISSION(102,"無權(quán)限"),
OUT_TIME(102,"登錄信息過期"),
DISABLE_ACCOUNT(102,"帳號(hào)已被禁用!"),
EMAIL_DISABLE_LOGIN(102,"該郵箱賬號(hào)已被管理員禁止登錄!"),
IP_REPEAT_SUBMIT(102,"訪問次數(shù)過多,請(qǐng)稍后重試"),
ERROR_DEFAULT(105,"系統(tǒng)繁忙,請(qǐng)稍后重試");
//異常碼
private int code;
//異常信息
private String msg;
//自定義方法
ExceptionCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
}當(dāng)我們對(duì)異常通過以上工具類進(jìn)行封裝之后,所有異常將以一種固定的格式返回,不會(huì)導(dǎo)致錯(cuò)亂:
{3 items
"code":105
"msg":"系統(tǒng)繁忙,請(qǐng)稍后重試"
"data":NULL
}異常處理
在spring boot中需要使用異常攔截器,攔截全局的異常,不直接將異常返回,而是在我們進(jìn)行處理之后,以一種清晰可讀的方式返回。并且前端能夠清晰解讀我們的異常,呈現(xiàn)給用戶。
//捕獲校驗(yàn)器異常
@RestControllerAdvice
public class ControllerExceptionAdvice {
@ExceptionHandler({BindException.class})
public ResultVo ValidExceptionHandler(BindException e) {
// 從異常對(duì)象中拿到ObjectError對(duì)象
ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
return new ResultVo(ResultCode.VALIDATE_ERROR.getCode(),objectError.getDefaultMessage());
}
}對(duì)特定異常進(jìn)行攔截,并包裝異常:
/**
* 對(duì)返回結(jié)果進(jìn)行包裝
*/
@RestControllerAdvice(basePackages = {"channel.cert"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
// response是ResultVo類型,或者注釋了NotControllerResponseAdvice都不進(jìn)行包裝
return !methodParameter.getParameterType().isAssignableFrom(ResultVo.class);
}
@Override
public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {
// String類型不能直接包裝
if (returnType.getGenericParameterType().equals(String.class)) {
ObjectMapper objectMapper = new ObjectMapper();
try {
// 將數(shù)據(jù)包裝在ResultVo里后轉(zhuǎn)換為json串進(jìn)行返回
return objectMapper.writeValueAsString(new ResultVo(data));
} catch (JsonProcessingException e) {
throw new APIException(ResultCode.RESPONSE_PACK_ERROR, e.getMessage());
}
}
// 否則直接包裝成ResultVo返回
return new ResultVo(data);
}
}異常捕捉
自定義異常:
@Getter
public class APIException extends RuntimeException {
private int code;
private String msg;
//自定義異枚舉
public APIException(StatusCode statusCode){
super(statusCode.getMsg());
this.code = statusCode.getCode();
this.msg = statusCode.getMsg();
}
// 手動(dòng)設(shè)置異常
public APIException(StatusCode statusCode, String message) {
// message用于用戶設(shè)置拋出錯(cuò)誤詳情,例如:當(dāng)前價(jià)格-5,小于0
super(message);
// 狀態(tài)碼
this.code = statusCode.getCode();
// 狀態(tài)碼配套的msg
this.msg = statusCode.getMsg();
}
// 默認(rèn)異常使用APP_ERROR狀態(tài)碼
public APIException(String errorMsg) {
super(errorMsg);
this.code = ExceptionCode.ERROR_DEFAULT.getCode();
this.msg = ExceptionCode.ERROR_DEFAULT.getMsg();
}
//自定義參數(shù) 錯(cuò)誤碼 錯(cuò)誤信息
public APIException(int errorCode, String errorMsg) {
super(errorMsg);
this.code = errorCode;
this.msg = ExceptionCode.ERROR_DEFAULT.getMsg();
}
//自定義參數(shù) 錯(cuò)誤碼 錯(cuò)誤信息 異常
public APIException(int errorCode, String errorMsg, Throwable cause) {
super(errorMsg);
this.code = errorCode;
this.msg = errorMsg;
}
}對(duì)自定義異常進(jìn)行捕獲,通過定義好的異常的結(jié)果集返回。
/**
* 全局異常處理
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {
// Assert業(yè)務(wù)異常
@ExceptionHandler(IllegalArgumentException.class)
public ResultVo AssertExceptionHandler(IllegalArgumentException ex) {
log.error( " msg : " + ex.getMessage(), ex);
if(StringUtils.isBlank(ex.getLocalizedMessage())){
return new ResultVo(ExceptionCode.ERROR_DEFAULT);
}
return new ResultVo(ex.getMessage());
}
// 登錄失效異常
@ExceptionHandler(SaTokenException.class)
public ResultVo LoginOutExceptionHandler(SaTokenException ex) {
log.error( " msg : " + ex.getMessage(), ex);
return new ResultVo(ExceptionCode.OUT_TIME);
}
// 登錄異常
@ExceptionHandler(NotLoginException.class)
public ResultVo NotLoginExceptionHandler(NotLoginException ex) {
log.error( " msg : " + ex.getMessage(), ex);
return new ResultVo(ExceptionCode.NOT_LOGIN);
}
// 權(quán)限異常
@ExceptionHandler(NotPermissionException.class)
public ResultVo NotPermissionExceptionHandler(NotPermissionException ex) {
log.error( " msg : " + ex.getMessage(), ex);
return new ResultVo(ExceptionCode.NO_PERMISSION);
}
//角色異常
@ExceptionHandler(NotRoleException.class)
public ResultVo NotRoleExceptionHandler(NotRoleException ex) {
log.error( " msg : " + ex.getMessage(), ex);
return new ResultVo(ExceptionCode.NO_Role);
}
//處理自定義異常
@ExceptionHandler(APIException.class)
public ResultVo APIExceptionHandler(APIException e) {
log.error(e.getMessage(), e);
return new ResultVo(e.getCode(), e.getMsg());
}
//處理運(yùn)行異常
@ExceptionHandler(RuntimeException.class)
public ResultVo RuntimeExceptionHandler(RuntimeException e) {
log.error(e.getMessage(), e);
return new ResultVo(ExceptionCode.ERROR_DEFAULT);
}
}通過以上自定義,我們就能在項(xiàng)目中,使用自定義異常,在我們認(rèn)為可能的報(bào)錯(cuò)處插入,當(dāng)發(fā)生錯(cuò)誤時(shí),我們更快定位是之前記錄的錯(cuò)誤點(diǎn)導(dǎo)致。
//查詢數(shù)字證書
@Override
public GroupUser searchCert(String certCode) {
try {
//查詢證書編號(hào)
GroupUser user = queryCert(certCode);
if(ObjectUtil.isNotNull(user)){
return user;
}
}catch (Exception e){
throw new APIException("區(qū)塊鏈調(diào)用失敗"+e);
}
return null;
}以上就是SpringBoot優(yōu)雅地實(shí)現(xiàn)異常處理的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot全局異常處理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring cloud gateway請(qǐng)求跨域問題解決方案
這篇文章主要介紹了spring cloud gateway請(qǐng)求跨域問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
理解Java中的靜態(tài)綁定和動(dòng)態(tài)綁定
這篇文章主要幫助大家理解Java中的靜態(tài)綁定和動(dòng)態(tài)綁定,在Java中存在兩種綁定方式,一種為靜態(tài)綁定,另一種就是動(dòng)態(tài)綁定,亦稱為后期綁定,感興趣的小伙伴們可以參考一下2016-02-02
Spring Cloud應(yīng)用實(shí)現(xiàn)配置自動(dòng)刷新過程詳解
這篇文章主要介紹了Spring Cloud應(yīng)用實(shí)現(xiàn)配置自動(dòng)刷新過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12

