@valid和自定義異常

@valid和自定義異常

問題的產生:

當有很多參數需要校驗時,比如name,age,email等很多參數都需要判空,或者有長度限制時,如果後端寫很多if-else就有很多代碼,不美觀,不優雅.前端每個參數都效驗的話工作量也很大

本文旨在解決這個問題,本文使用@valid 註解來解決這個問題.

首先定義一個

統一結果返回

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {

    private String msg;
    private Integer code;
    private T data;
    private static final Integer successCode = 200;
    private static Integer errorCode = 500;
    private static final String successMsg = "成功";
    private static final Object resultNoData = null;

    public static Result successNoResult() {
        return new Result(successMsg, successCode, resultNoData);
    }

    public static <T> Result<T> successWithResult(T data) {
        return new Result(successMsg, successCode, data);
    }

    public static Result successWithCodeAndMsg(Integer code, String msg) {
        return new Result(msg, code, resultNoData);
    }

    public static Result errorNoResult(String msg) {
        return new Result(msg, errorCode, resultNoData);
    }

    public static Result errorWithResult(String msg, Object data) {
        return new Result(msg, errorCode, data);
    }

    public static Result errorWithCodeAndMsg(Integer code, String msg) {
        return new Result(msg, code, resultNoData);
    }

}

@valid 註解簡單使用

先看下簡單使用,複雜的自己查api吧

首先在控制層的參數上加上該註解

import javax.validation.Valid;
import java.util.*;

@RestController
@RequestMapping("/test")
public class Test2 {

    @RequestMapping("test2")
    public Result<User> test2(@Valid User user){
        return Result.successWithResult(user);
    }
}

然後在實體類中加上如下註解

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class User {
    
    /**
     * @NotEmpty:不能爲null,而且長度必須大於0
     * @NotBlank:只用在String上,表示傳進來的值不能爲null,而且調用trim()後,長度必須大於0
     * @NotNull:不能爲null,但可以爲empty
     * @Length(message = "名稱不能超過個 {max} 字符", max = 10)
     * @Range(message = "年齡範圍爲 {min} 到 {max} 之間", min = 1, max = 100)
     */
    @NotNull( message = "ID不能爲空")
    private Integer id;

    @NotBlank( message = "暱稱不能爲空")
    @Size( min = 2,max = 5,message ="暱稱的字數個數必須在0和5之間" )
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

請求該接口

http://localhost:8080/test/test2?id=5&name=xxwwwww

查看效果,如下

image

可以看到,已經能表示該請求參數錯誤

但是還有個問題,我們不能直接這樣子直接返回給前端,需要返回統一的結果

全局異常處理

Spring-boot對於異常的處理也做了不錯的支持,

它提供了一個 @ControllerAdvice註解以及 @ExceptionHandler註解,

前者是用來開啓全局的異常捕獲,後者則是說明捕獲哪些異常,對那些異常進行處理。如下

自定義異常

import lombok.Data;

@Data
public class DefinitionException extends RuntimeException {
    private Integer errorCode;
    private String errorMsg;

    public DefinitionException(){

    }
    public DefinitionException(Integer errorCode, String errorMsg) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

}

異常處理

@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 處理自定義異常
     */
    @ExceptionHandler(value = DefinitionException.class)
    @ResponseBody
    public Result bizExceptionHandler(DefinitionException definitionException) {
        Result result=new Result();
        result.setCode(definitionException.getErrorCode());
        result.setMsg(definitionException.getErrorMsg());
        return result;
    }

    /**
     * 處理異常
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result runtimeExceptionHandler(Exception exception) {
        Result result=new Result();
        result.setCode(500);
        result.setMsg(exception.getMessage());
        return result;
    }
}

測試代碼

@RequestMapping("test3")
public Result<String> test3(){
int i=1/0;
return Result.successWithResult("test3");
}

@RequestMapping("test4")
public Result<String> test4(){
throw new DefinitionException(500,"啊哦,報錯了");
}

查看結果

image

image

將@valid註解拋的異常也返回統一格式

我們再請求一下這個接口

http://localhost:8080/test/test2?id=5&name=xxwwwww

看下結果

image

返回格式是統一的格式了,但是返回的信息不太友好,我們看看怎麼優化

debug一下,看看這個是什麼異常

image

我們看到,這個異常是org.springframework.validation.BindException類的,

我們看下這個類的具體內容,我們只要我們想要的信息就行

image

這裏,我們只要這個信息就可以了我們改動後如下

    /**
     * 處理異常
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result runtimeExceptionHandler(Exception exception) {
        Result result=new Result();
        if(exception instanceof BindException){//註解類異常
            StringBuilder sb = new StringBuilder();
            BindException bindException= (BindException) exception;
            BindingResult bindingResult = bindException.getBindingResult();
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            for (ObjectError item : allErrors) {
                sb
                        .append(item.getDefaultMessage())
                        .append(',');
            }
            sb.deleteCharAt(sb.length()-1);
            result.setCode(500);
            result.setMsg(sb.toString());

        }
        return result;
    }

再請求該接口,得到結果

image

到此爲止,我們已經得到了我們想要的結果

優化代碼

最後,我們在優化一下全局異常處理代碼如下

import com.yoocar.util.Result;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;

@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 處理自定義異常
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result exceptionHandler(Exception exception) {
        Result result=new Result();
        //自定義類型異常
        if(exception instanceof DefinitionException){
            DefinitionException definitionException= (DefinitionException) exception;
            result.setCode(definitionException.getErrorCode());
            result.setMsg(definitionException.getErrorMsg());
        }else if(exception instanceof BindException){//@valid註解拋出的異常
            //使用StringBuilder來拼接錯誤信息,減少對象開銷
            StringBuilder stringBuilder = new StringBuilder();
            //獲取並拼接所有錯誤信息
            BindException bindException= (BindException) exception;
            BindingResult bindingResult = bindException.getBindingResult();
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            for (ObjectError item : allErrors) {
                stringBuilder.append(item.getDefaultMessage())
                        .append(',');
            }
            //刪除最後一個逗號
            stringBuilder.deleteCharAt(stringBuilder.length()-1);
            result.setCode(600);//這裏自定義了600用於表示參數有誤
            result.setMsg(stringBuilder.toString());
        }else {//其他異常
            result.setCode(500);
            result.setMsg(exception.getMessage());
        }
        return result;
    }

}

至此,本文結束.文章中若有錯誤和疏漏之處,還請各位大佬不吝指出,謝謝大家!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章