SpringBoot進行優雅的全域性異常處理

LeoMalik發表於2020-02-25

SpringBoot進行優雅的全域性異常處理

在專案中經常出現系統異常的情況,比如NullPointerException等等。如果預設未處理的情況下,springboot會響應預設的錯誤提示,這樣對使用者體驗不是友好,系統層面的錯誤,使用者不能感知到,即使為500的錯誤,可以給使用者提示一個類似伺服器開小差的友好提示等。這時候便可以使用全域性異常處理器來優雅的處理全域性異常

定義錯誤訊息類

  • 在全域性異常類中會使用該錯誤訊息進行初始化

  • 注意該訊息的toString方法要寫成Json格式的

  • 具體的錯誤訊息初始化的時候也可以使用%s一類的佔位符,後期呼叫靜態方法,來填充多個引數

  • 這裡使用了@Data註解省去了自己編寫建構函式,詳情可以查詢lombook使用方法

@Data
@AllArgsConstructor
@Slf4j
public class CodeMsg implements Serializable {

    private int code;
    private String msg;

    public static CodeMsg SUCCESS = new CodeMsg(200, "success");
    public static CodeMsg SERVER_ERROR = new CodeMsg(500100, "伺服器錯誤");
    public static CodeMsg INSERT_ERROR = new CodeMsg(500200, "插入錯誤");
    public static CodeMsg UNKNOWN_ERROR = new CodeMsg(-999, "未知錯誤:%s");
    //填充多個錯誤資訊
    public static CodeMsg BIND_ERROR = new CodeMsg(500300,"引數校驗異常:%s");
    public static CodeMsg UNSELECT_FILE_ERROR = new CodeMsg(500400,"未選擇檔案");
    public static CodeMsg FILE_NOT_EXIST_ERROR = new CodeMsg(500500, "檔案或資料夾不存在");
    public static CodeMsg UPLOADING = new CodeMsg(500600, "正在上傳");


    //補充未知錯誤的具體資訊
    public CodeMsg fillArgs(Object... args){
        int code =  this.code;
        String message = String.format(this.msg,args);
        return new CodeMsg(code,message);
    }

    //處理異常時返回json的toString
    @Override
    public String toString() {
        return "CodeMsg{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                '}';
    }

    // public static void main(String[] args) {
    //     log.info(new CodeMsg(0, "hello").toString());
    // }
}
複製程式碼

定義全域性異常類

  • 之後遇到什麼錯誤,可以用錯誤訊息類初始化全域性異常類,並丟擲該異常,該異常之後會被全域性異常處理器捕獲
//繼承RuntimeException,並定義serialVersionUID
public class GlobalException extends RuntimeException {

    private static final long serialVersionUID = -3586828184536704147L;
    private CodeMsg codeMsg;

    public GlobalException(CodeMsg codeMsg) {
        super(codeMsg.toString());
        this.codeMsg = codeMsg;
    }

    public CodeMsg getCodeMsg() {
        return codeMsg;
    }
}

複製程式碼

定義返回結果類

  • 返回結果有兩種,一種是返回相應的錯誤碼,一種是返回正確的驗證碼+資料,因此我們編寫返回結果類
@Data
@ApiModel(description = "返回結果")
public class Result<T> implements Serializable {
    private int code;
    private T data;
    private String msg;

    private Result(T data) {
        this.code = 0;
        this.msg = "success";
        this.data = data;
    }

    private Result() {
        this.code = 0;
        this.msg = "success";
        this.data = null;
    }

    private Result(CodeMsg codeMsg) {
        if (codeMsg == null) {
            return;
        }
        this.code = codeMsg.getCode();
        this.msg = codeMsg.getMsg();
    }


    public static <T> Result<T> success(T data) {
        return new Result<T>(data);
    }

    public static <T> Result<T> success() {
        return new Result<T>();
    }
    public static <T> Result<T> error(CodeMsg codeMsg) {
        return new Result<T>(codeMsg);
    }

}
複製程式碼

定義全域性異常處理器

  • 使用@ControllerAdvice來指定全域性異常處理

  • 使用@RestController返回json資料

  • 使用@ExceptionHandler來捕獲特定的異常種類

@ControllerAdvice
@RestController
@Slf4j
public class GlobalExceptionHandler {

    //自定義異常類
    @ExceptionHandler(value = GlobalException.class)
    public Result<String> globalExceptionHandler(HttpServletRequest request, GlobalException e) {
        log.error(e.getCodeMsg().getMsg());
        return Result.error(e.getCodeMsg());
    }

    //引數檢測不合格
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result<String> bindExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException e) {
        log.error(e.getClass().toString());
        // log.error(e.toString());
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        StringBuilder errorMsg = new StringBuilder();
        //此處僅取第一個
        // ObjectError objectError = errors.get(0);
        for (ObjectError error : errors) {
            errorMsg.append(error.getDefaultMessage()).append(" ");
        }
        // String errorMsg = objectError.getDefaultMessage();
        //填充具體異常
        return Result.error(CodeMsg.BIND_ERROR.fillArgs(errorMsg.toString()));
    }

    //其他異常類
    @ExceptionHandler(value = Exception.class)
    public Result<String> defaultExceptionHandler(HttpServletRequest request, Exception e) {
        log.error(e.getClass().toString());
        log.error(e.toString());
        e.printStackTrace();
        return Result.error(CodeMsg.UNKNOWN_ERROR.fillArgs(e.getClass().toString()));
    }

    //可定義詳細的其他異常類
    // @ExceptionHandler(AuthenticationException.class)   //此處為shiro未登入異常類
    // @ResponseStatus(HttpStatus.UNAUTHORIZED)
    // public String unAuth(AuthenticationException e) {
    //     log.error("使用者未登陸:", e);
    //     return "/login.html";
    // }
}
複製程式碼

效果

至此,便完成了自定義的全域性異常處理方法,訪問效果如下

 //成功
 {
    code = 0;
    msg = "success";
    data = 456;
 }
 
 //失敗
 {
 	code = 500100;
 	msg = "伺服器錯誤";
 	data = null;
 }
複製程式碼

相關文章