Java SpringBoot上的引數校驗JSR 303 Validation

飛一樣的豬發表於2018-06-24

最近開始由原來的Android工程師Java Web向轉型了,廢話不多說,上圖

背景

JSR 303 – Bean Validation 是一個資料驗證的規範,2009 年 11 月確定最終方案。 Hibernate Validator 是 Bean Validation 的參考實現 . Hibernate Validator 提供了 JSR 303 規範中所有內建 constraint 我們通常寫介面時會對傳來的引數進行校驗判斷,比如字串非空判斷,值在多少返回等,這些就要用到Spring 的引數校驗,這裡我們使用在spring-boot-starter-web包裡面有hibernate-validator包, 引數校驗有幾種方式,如下

  • 1.使用@Valid+BindingResult
  • 2.註解使用@Valid +全域性異常捕捉處理

下面來介紹這兩種方式的使用

使用@Valid+BindingResult 引數驗證

1.符合國際JSP 303規範,先定義個Bean,別人在欄位名上新增驗證的註解,@NotBlank 欄位名不能為null,同時長度大於0常有的校驗型別有

Constraint    詳細資訊

@Null    被註釋的元素必須為 null
@NotNull    被註釋的元素必須不為 null
@AssertTrue    被註釋的元素必須為 true
@AssertFalse    被註釋的元素必須為 false
@Min(value)    被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value)    被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value)    被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value)    被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max, min)    被註釋的元素的大小必須在指定的範圍內
@Digits (integer, fraction)    被註釋的元素必須是一個數字,其值必須在可接受的範圍內
@Past    被註釋的元素必須是一個過去的日期
@Future    被註釋的元素必須是一個將來的日期
@Pattern(value)    被註釋的元素必須符合指定的正規表示式
複製程式碼
/**
 * @author Lang.Chen
 * @date 2018/6/20 下午4:39
 */
public class User {

    //賬號
    @NotBlank(message = "賬號不能為空")
    private String phone;
    //密碼
    @NotBlank(message = "密碼不能為空")
    private String password;

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public String getPassword() {
        return password;
    }
}



複製程式碼

2.在Controller類裡面對相應的介面新增@Valid+BindingResult驗證,加完以後,如果引數驗證不通過,那就直接進入if語句裡面,在語句裡面做相應的返回結果

 if(bindingResult.hasErrors()){

 }

複製程式碼
@RestController
@RequestMapping(value = "/user")
public class UserController implements IUser {


 @Override
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public User login( @Valid User userInfo, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            System.out.print(bindingResult.getFieldError().getDefaultMessage());
            return null;
        }
        return userInfo;
    }

}


複製程式碼

好了,第一種驗證我們已經說完了,但你仔細想想,如果我有多個介面,是不是每次有要寫@Valid+BindgResult bingingResult,然後再到If語句裡面進行判斷,如果僅僅是錯誤資訊不一致,但返回的客戶端結構是一致的,比如


 {"code":"1002","message":"parameters are missing","data":"{}"}
 
複製程式碼

不同的驗證只要改變message,那麼是不是可以統一處理,來減少程式碼量,於是下面介紹第二種方式

使用@Valid+全域性異常捕捉 引數校驗

簡單點說,由第一種方式去掉BindingResult,然後再定義一個全域性異常類,同樣的

1.還是定義POJO

/**
 * @author Lang.Chen
 * @date 2018/6/20 下午4:39
 */
public class User {

    //賬號
    @NotBlank(message = "賬號不能為空")
    private String phone;
    //密碼
    @NotBlank(message = "密碼不能為空")
    private String password;

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public String getPassword() {
        return password;
    }
}
複製程式碼

2.在Controller裡面的方法定義要驗證的@Valid


public class UserController implements IUser {

 @Override
    @RequestMapping(value = "/login2", method = RequestMethod.POST)
    public User login2(@RequestBody @Valid User userInfo) {
        return null;
    }

 }   

複製程式碼

3.定義全域性異常類

所有驗證失敗的結果都會在GlobleExceptionHandler的defultExcepitonHandler方法裡面捕捉到


@RestControllerAdvice
@Component
public class GlobleExceptionHandler {



    @ResponseBody
    @ExceptionHandler(Exception.class)
    public String defultExcepitonHandler(Exception ex) {

        ex.printStackTrace();
        if(ex instanceof  BindException){
            //處理返回的錯誤資訊
            StringBuffer errorMsg = new StringBuffer();
            BindException c = (BindException) ex;
            List<ObjectError> errors = c.getBindingResult().getAllErrors();
            for (ObjectError error : errors) {
                errorMsg.append(error.getDefaultMessage()).append(";");
            }

            return errorMsg.toString();

        }
        return "";

    }

}

複製程式碼

有關於自定義驗證和分組驗證,就不一一介紹了,會使用以上2個就可以了。

Q&A

1.如何使用單個引數驗證,並且進入全域性異常捕捉,返回?

有時候只傳入少數驗證時,我們不可能都是新建一個物件來接受,這時候如何使用單個引數驗證呢,看到網上使用在類上新增 @Validated引數,然後通過全域性異常捕捉方式

@RestController
@RequestMapping("/order")
@Validated
public class OrderController implements IOrder {


   @RequestMapping(value = "/byId",method = RequestMethod.POST)
   public OrderInfo byId( @Max(value = 50) int orderId) {
       return null;
   }

}
複製程式碼

但每次訪問介面時,都是報同一個錯誤,就沒用這個方式了,如果有人找到解決方案,請指教,謝謝

{
    "timestamp": "2018-06-24T07:09:14.235+0000",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/order/byId"
}

複製程式碼

@Valid和@Validated的區別

@Valid是javax.validation裡的 @Validated是@Valid 的一次封裝,是Spring提供的校驗機制使用。@Validated 提供分組功能

相關文章