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è)合適 的異常處理類,在之后的異常都會以這種格式返回前端,前端根據(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; // 返回對象 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ù)對象 public ResultVo(Object data) { this.code = ResultCode.SUCCESS.getCode(); this.msg = ResultCode.SUCCESS.getMsg(); this.data = data; } // 返回指定狀態(tài)碼,數(shù)據(jù)對象 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)級別錯(cuò)誤碼 ERROR(-1, "操作異常"), NOT_LOGIN(102, "請先登錄!"), NO_Role(102,"無權(quán)限"), NO_PERMISSION(102,"無權(quán)限"), OUT_TIME(102,"登錄信息過期"), DISABLE_ACCOUNT(102,"帳號已被禁用!"), EMAIL_DISABLE_LOGIN(102,"該郵箱賬號已被管理員禁止登錄!"), IP_REPEAT_SUBMIT(102,"訪問次數(shù)過多,請稍后重試"), ERROR_DEFAULT(105,"系統(tǒng)繁忙,請稍后重試"); //異常碼 private int code; //異常信息 private String msg; //自定義方法 ExceptionCode(int code, String msg) { this.code = code; this.msg = msg; } }
當(dāng)我們對異常通過以上工具類進(jìn)行封裝之后,所有異常將以一種固定的格式返回,不會導(dǎo)致錯(cuò)亂:
{3 items "code":105 "msg":"系統(tǒng)繁忙,請稍后重試" "data":NULL }
異常處理
在spring boot中需要使用異常攔截器,攔截全局的異常,不直接將異常返回,而是在我們進(jìn)行處理之后,以一種清晰可讀的方式返回。并且前端能夠清晰解讀我們的異常,呈現(xiàn)給用戶。
//捕獲校驗(yàn)器異常 @RestControllerAdvice public class ControllerExceptionAdvice { @ExceptionHandler({BindException.class}) public ResultVo ValidExceptionHandler(BindException e) { // 從異常對象中拿到ObjectError對象 ObjectError objectError = e.getBindingResult().getAllErrors().get(0); return new ResultVo(ResultCode.VALIDATE_ERROR.getCode(),objectError.getDefaultMessage()); } }
對特定異常進(jìn)行攔截,并包裝異常:
/** * 對返回結(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; } }
對自定義異常進(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 { //查詢證書編號 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全局異常處理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot導(dǎo)入導(dǎo)出數(shù)據(jù)實(shí)現(xiàn)方法詳解
這篇文章主要介紹了SpringBoot導(dǎo)入導(dǎo)出數(shù)據(jù)實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-12-12通過spring注解開發(fā),簡單測試單例和多例區(qū)別
這篇文章主要介紹了通過spring注解開發(fā),簡單測試單例和多例區(qū)別,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08MyBatis-Plus雪花算法實(shí)現(xiàn)源碼解讀
雪花算法是一種用于生成唯一標(biāo)識符(ID)的分布式算法,雪花算法的設(shè)計(jì)目標(biāo)是在分布式系統(tǒng)中生成全局唯一的ID,同時(shí)保證ID的有序性和趨勢遞增,這篇文章主要介紹了MyBatis-Plus雪花算法實(shí)現(xiàn)源碼解析,需要的朋友可以參考下2023-12-12java實(shí)現(xiàn)簡單的圖書管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單的圖書管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07Java利用Dijkstra算法求解拓?fù)潢P(guān)系最短路徑
迪杰斯特拉算法(Dijkstra)是由荷蘭計(jì)算機(jī)科學(xué)迪家迪杰斯特拉于1959年提出的,因此又叫狄克斯特拉算法。本文將利用迪克斯特拉(Dijkstra)算法求拓?fù)潢P(guān)系最短路徑,感興趣的可以了解一下2022-07-07一步步教你JAVA如何優(yōu)化Elastic?Search
想要榨干Java操作Elasticsearch的所有性能潛力?本指南將一步步教你如何優(yōu)化Java與Elasticsearch的交互!從此,提升ES查詢速度、降低資源消耗不再是難題,趕快一起來探索Java?Elasticsearch優(yōu)化的秘訣吧!2024-01-01Java8深入學(xué)習(xí)系列(二)函數(shù)式編程
函數(shù)式編程,這個(gè)詞語由兩個(gè)名詞構(gòu)成,函數(shù),編程。編程這個(gè)詞我就不用解釋了,大家都是做這個(gè)的。函數(shù),其實(shí)單獨(dú)抽離出來這個(gè)詞語,也并不陌生,那二者組合后的到底是什么呢,下面這篇文章主要給大家介紹了關(guān)于Java8函數(shù)式編程的相關(guān)資料,需要的朋友可以參考下。2017-08-08SpringMVC之RequestContextHolder詳細(xì)解析
這篇文章主要介紹了SpringMVC之RequestContextHolder詳細(xì)解析,正常來說在service層是沒有request的,然而直接從controlller傳過來的話解決方法太粗暴,后來發(fā)現(xiàn)了SpringMVC提供的RequestContextHolder,需要的朋友可以參考下2023-11-11java中LinkedBlockingQueue與ArrayBlockingQueue的異同
這篇文章主要介紹了java中LinkedBlockingQueue與ArrayBlockingQueue的異同,需要的朋友可以參考下2016-08-08