上一篇文章說到,參數校驗,每每須要和全局的異常攔截器來配套使用,使得返回的數據結構永遠是保持一致的。參數異常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)來講,最好返回值的結構都是一致的,這樣才方便解釋。java
public class BaseResult {
private int code;
private String message;
private Object data;
// 省略getter setter方法,全參構造方法
}
複製代碼
無論什麼接口,都採用這樣的數據結構返回給前端。好比約定code爲0時是成功,其餘錯誤定義出具體的錯誤碼,message放錯誤信息,data對象放相應的數據。web
@RestControllerAdvice
public class GlobalExceptionHandlerAdvice {
}
複製代碼
使用RestControllerAdvice能夠標識一個類爲異常捕獲類。正則表達式
經過參數異常的測試,能夠知道參數有異常時會拋出org.springframework.web.bind.MethodArgumentNotValidException。咱們如今手動捕獲 這個異常,而且返回一個BaseResult格式的響應。spring
@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
效果以下:bash
返回的結果就比較友好了,前端處理起來也方便。數據結構
使用異常來處理業務邏輯,會使代碼寫起來更加流暢。好比說,一個刪除用戶數據的方法,返回值爲void(無返回值),可是當傳入的用戶id不存在的時候,就應該返回一個用戶不存在的結果,這對於void返回值的方法來講,顯得無能爲力。可是,使用異常流來處理該業務邏輯,會變得很是簡單。咱們直接拋出一個自定義異常,而後在異常捕獲器上捕獲該異常,再把結果返回給前端便可。app
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方法,重寫該方法是不保留異常信息棧。由於咱們使用該異常來處理業務邏輯,都是咱們手動拋出的,因此也不須要保存異常信息棧了,這會提高性能。ide
@ExceptionHandler(WebException.class)
public BaseResult handleWebException(WebException e) {
return new BaseResult(e.getCode(), e.getErrorMsg(), null);
}
複製代碼
在以前的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時,提示用戶不存在
當userId爲1時,提示成功.
這裏實現了全局異常捕獲,而且介紹了異常流處理業務邏輯。這裏只是一個小demo,還有不少待改進的地方。好比說,我沒有定義一個錯誤碼枚舉類。在定義了錯誤碼枚舉類的前提下,修改構造BaseResult的模式,能夠採用靜態工廠模式來構造等。這裏就不展開討論了。