SpringBoot 統一異常處理
1.前言
在做專案的時候,常常會遇到這些情況,
(一)、繁多的if..else 造成大量的引數程式碼判斷,於是就在實體類欄位中新增@NotBlank,@NotNull等註解代替,可是註解上自定義的message 訊息無法規範的返回到前端;
(二)、在業務層程式碼中,當方法層層巢狀,對最深處的程式碼進行不滿足的引數做判斷時,直接返回響應體並不是很合適(這個時候就需要丟擲自定義異常)
(三)、通過Assert 斷言去除冗餘的if..else 時,發現斷言丟擲的異常也沒有規範的返回前端;
為了解決這些情況,我們需要做一個統一異常處理,無論是註解的異常丟擲,自定義異常丟擲、以及斷言的異常丟擲都需要統一獲取其中的message。這個統一異常處理即今天所要說的 @ExceptionHandler+@ControllerAdvice ,統一異常處理。
2.直接實戰
原理也不解釋,廢話也不多說,直接記錄怎麼配置,怎麼使用。想知道原理的,自行百度。
2.1.建立專案
通過IDEA 中的SpringInitializr 建立springboot專案,並一次建立各個層的資料夾,controller、service、entity、model、exception、handler等資料夾。如下所示:
2.2 統一異常配置
2.2.1 Result 響應實體類,用來返回前端的實體類。
/** * @Author: DZBiao * @Date : 2021/12/11 * @Description : 描述: **/ public class Result { /** * 響應狀態碼 */ private Integer code; /** * 響應成功與否 */ private boolean success; /** * 響應訊息 */ private String msg; /** * 響應資料 */ private Object data; public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Result() { } public Result(Integer code, boolean success, String msg) { this.code = code; this.success = success; this.msg = msg; } public Result(Integer code, boolean success, String msg, Object data) { this.code = code; this.success = success; this.msg = msg; this.data = data; } /** * 成功 返回預設成功資訊 * * @return */ public static Result SUCCESS() { return new Result(1, true, "操作成功", null); } /** * 成功 返回(data資料)成功資訊 * * @param data * @return */ public static Result SUCCESS(Object data) { return new Result(1, true, "操作成功", data); } /** * 成功 返回自定義(訊息、data資料)成功資訊 * * @param msg * @param data * @return */ public static Result SUCCESS(String msg, Object data) { return new Result(1, true, msg, data); } /** * 失敗 返回預設失敗資訊 * * @return */ public static Result ERROR() { return new Result(-1, false, "操作失敗", null); } /** * 失敗 返回自定義(訊息)失敗資訊 * * @param msg * @return */ public static Result ERROR(String msg) { return new Result(-1, false, msg, null); } /** * 失敗 返回自定義(訊息、狀態碼)失敗資訊 * * @param code * @param msg * @return */ public static Result ERROR(Integer code, String msg) { return new Result(code, false, msg, null); } }
2.2.2 @Controller 和 @RestControllerAdvice 配置
在controller層中新建IndexController類,傳參實體類使用簡單的User類;
@RestController @RequestMapping("/index") public class IndexController { @PostMapping("/list") public Result index(@Valid @RequestBody User user) throws Exception { return Result.SUCCESS(); } }
實體類User ,當我想要在傳參時進行非空判斷,則新增@NotBlank註解,並在Controller層 新增@Valid和@RequestBody 註解。(@Data 註解為Lombok 外掛,idea中安裝,如果沒有直接在實體類中生成get和set 方法代替.)
@Data
public class User {
private String id ; // ID
@NotBlank(message = "使用者名稱不能為空.")
private String username ; // 使用者名稱
@NotBlank(message = "性別不能為空")
private String sex ; // 性別
@NotBlank(message = "地址不能為空")
private String address ; // 地址
private String status ;
}
在handler 資料夾中新建GlobalExceptionController類,並配置@RestControllerAdvice註解。對於實體類引數上的註解的異常捕獲,我們在統一異常處理類中,通過MethodArgumentNotValidException進行捕獲處理。
@RestControllerAdvice
public class GlobalExceptionController {
/**
* 處理方法引數異常,如 實體類上的@NotBlank註解異常,@NotNull註解等異常資訊返回。
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleMethodArgumentException(MethodArgumentNotValidException ex){
return Result.ERROR(ex.getBindingResult().getFieldError().getDefaultMessage()) ;
}
}
至此我們解決了三個問題中的其中一個問題。下面解決丟擲自定義異常 和斷言異常的捕獲。
在GlobalExceptionController中再新增 處理自定義異常和斷言異常的捕獲方法。
@RestControllerAdvice
public class GlobalExceptionController {
/**
* 處理方法引數異常,如 實體類上的@NotBlank註解異常,@NotNull註解等異常資訊返回。
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleMethodArgumentException(MethodArgumentNotValidException ex){
return Result.ERROR(ex.getBindingResult().getFieldError().getDefaultMessage()) ;
}
/**
* 斷言、自定義異常等處理
* @param ex
* @return
*/
@ExceptionHandler(Exception.class)
public Result handleException(Exception ex){
return Result.ERROR(ex.getMessage()) ;
}
}
3.測試
我們在exception資料夾中建立自定義異常類:CustomException,並繼承RuntimeException
public class CustomException extends RuntimeException {
private String message ;
public CustomException(String message){
this.message = message ;
}
@Override
public String getMessage() {
return message;
}
public CustomException setMessage(String message) {
this.message = message;
return this;
}
}
在controller中新增一些判斷進行觀察,看看我們配置的情況。
@RestController
@RequestMapping("/index")
public class IndexController {
@PostMapping("/list")
public Result index(@Valid @RequestBody User user) throws Exception {
// 丟擲Exception異常
if (user.getUsername().equals("xxx")){
throw new Exception("使用者名稱錯誤.");
}
// 自定義異常
if (user.getUsername().equals("xxxxx")){
throw new CustomException("自定義異常");
}
// 斷言異常
Assert.notNull(user.getStatus(), "狀態不能為空");
return Result.SUCCESS();
}
}
我們通過Postman或者ApiPost觀察。當我們什麼引數都不傳時,就會捕獲註解上的異常message,當username傳“xxx”,就會捕獲使用者名稱錯誤.異常,同理,可以觀察到其他的情況,不多贅述。
統一異常處理配置很簡單。至此配置完成。
通過統一異常處理的配置,我們可以去除大量的if..else 冗餘程式碼,全部交由註解或者Assert斷言,或者自定義異常來解決。