1. 程式人生 > 其它 >SpringBoot 統一異常處理

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斷言,或者自定義異常來解決。