springboot 全局異常捕獲,異常流處理業務邏輯


前言

上一篇文章說到,參數校驗,往往需要和全局的異常攔截器來配套使用,使得返回的數據結構永遠是保持一致的。參數異常springboot默認的返回結構:

{
 "timestamp": "2019-04-25T13:09:02.196+0000",
 "status": 400,
 "error": "Bad Request",
 "errors": [
 {
 "codes": [
 "Pattern.param.birthday",
 "Pattern.birthday",
 "Pattern.java.lang.String",
 "Pattern"
 ],
 "arguments": [
 {
 "codes": [
 "param.birthday",
 "birthday"
 ],
 "arguments": null,
 "defaultMessage": "birthday",
 "code": "birthday"
 },
 [],
 {
 "defaultMessage": "\d{4}-\d{2}-\d{2}",
 "codes": [
 "\d{4}-\d{2}-\d{2}"
 ],
 "arguments": null
 }
 ],
 "defaultMessage": "需要匹配正則表達式"\d{4}-\d{2}-\d{2}"",
 "objectName": "param",
 "field": "birthday",
 "rejectedValue": "apple",
 "bindingFailure": false,
 "code": "Pattern"
 }
 ],
 "message": "Validation failed for object='param'. Error count: 1",
 "path": "/validate/notblank"
}

不管是正常的情況,還是異常的情況,對於前端(或者app)來說,最好返回值的結構都是一致的,這樣才方便解釋。

定義一個BaseResult類,定義返回值的數據結構

public class BaseResult {
 private int code;
 private String message;
 private Object data;
 // 省略getter setter方法,全參構造方法
}

不管什么接口,都采用這樣的數據結構返回給前端。比如約定code為0時是成功,其他錯誤定義出具體的錯誤碼,message放錯誤信息,data對象放相應的數據。

定義全局異常處理器GlobalExceptionHandlerAdvice

@RestControllerAdvice
public class GlobalExceptionHandlerAdvice {

}

使用RestControllerAdvice可以標識一個類為異常捕獲類。

捕獲異常

通過參數異常的測試,可以知道參數有異常時會拋出org.springframework.web.bind.MethodArgumentNotValidException。我們現在手動捕獲 這個異常,並且返回一個BaseResult格式的響應。

@ExceptionHandler(MethodArgumentNotValidException.class)
public BaseResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
 BindingResult result = e.getBindingResult();
 FieldError fieldError = result.getFieldError();
 String defaultMessage = fieldError.getDefaultMessage();
 return new BaseResult(11000, defaultMessage, null);
}

使用ExceptionHandler可以捕獲異常類型。這里捕獲了參數錯誤會拋出的異常,然后返回了自定義的結果。這里錯誤碼為隨便填寫,真實開發,建議定義一個錯誤碼枚舉類。

效果如下:

【快學springboot】5.全局異常捕獲,異常流處理業務邏輯

 

返回的結果就比較友好了,前端處理起來也方便。

異常流處理業務邏輯

使用異常來處理業務邏輯,會使代碼寫起來更加流暢。比如說,一個刪除用戶數據的方法,返回值為void(無返回值),但是當傳入的用戶id不存在的時候,就應該返回一個用戶不存在的結果,這對於void返回值的方法來說,顯得無能為力。但是,使用異常流來處理該業務邏輯,會變得非常簡單。我們直接拋出一個自定義異常,然后在異常捕獲器上捕獲該異常,再把結果返回給前端即可。

定義一個WebException

public class WebException extends RuntimeException {
 private int code;
 private String errorMsg;
 public WebException(int code, String errorMsg) {
 super(errorMsg);
 this.code = code;
 this.errorMsg = errorMsg;
 }
 @Override
 public synchronized Throwable fillInStackTrace() {
 return this;
 }
 // 省略getter setter方法
}

這里定義了一個運行時異常,重寫了fillInStackTrace方法,重寫該方法是不保留異常信息棧。因為我們使用該異常來處理業務邏輯,都是我們手動拋出的,所以也不需要保存異常信息棧了,這會提升性能。

在異常捕獲器添加WebException異常捕獲

@ExceptionHandler(WebException.class)
public BaseResult handleWebException(WebException e) {
 return new BaseResult(e.getCode(), e.getErrorMsg(), null);
}

模擬一段業務邏輯,拋出WebException

在之前的UserController類,修改之前寫的deleteUser方法,如下:

@DeleteMapping(value = "/{userId}")
public Object deleteUser(@PathVariable(value = "userId") Integer userId) {
 if (userId == 0) {
 throw new WebException(-1, "用戶不存在");
 }
 return new BaseResult(1, "成功", null);
}

這里定義一個delete請求的接口,接收一個userId參數,如果userId等於0,則返回該用戶不存在。測試結果如下:

當userId為0時,提示用戶不存在

【快學springboot】5.全局異常捕獲,異常流處理業務邏輯

 

當userId為1時,提示成功.

【快學springboot】5.全局異常捕獲,異常流處理業務邏輯

 

總結

這里實現了全局異常捕獲,並且介紹了異常流處理業務邏輯。這里只是一個小demo,還有很多待改進的地方。比如說,我沒有定義一個錯誤碼枚舉類。在定義了錯誤碼枚舉類的前提下,修改構造BaseResult的模式,可以采用靜態工廠模式來構造等。這里就不展開討論了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM