Hibernate Validator校驗

1. 參數校驗


  • 官網地址

  • spring-boot-starter-web包裏面有hibernate-validator包,不需要引用hibernate validator依賴。

2. hibernate validator校驗demo


1. 導入包

import org.hibernate.validator.constraints.NotBlank;

import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.Pattern;

2. demo

@Getter
@Setter
@NoArgsConstructor
public class DemoModel {
    @NotBlank(message="用戶名不能爲空")
    private String userName;

    @NotBlank(message="年齡不能爲空")
    @Pattern(regexp="^[0-9]{1,2}$",message="年齡不正確")
    private String age;

    @AssertFalse(message = "必須爲false")
    private Boolean isFalse;
    /**
     * 如果是空,則不校驗,如果不爲空,則校驗
     */
    @Pattern(regexp="^[0-9]{4}-[0-9]{2}-[0-9]{2}$",message="出生日期格式不正確")
    private String birthday;
}

3. 結果返回

@RequestMapping("/demo2")
public void demo2(@RequestBody @Valid DemoModel demo, BindingResult result){
    if(result.hasErrors()){
        for (ObjectError error : result.getAllErrors()) {
            System.out.println(error.getDefaultMessage());
        }
    }
}

4. 傳入參數

{"userName":"dd","age":120,"isFalse":true,"birthday":"21010-21-12"}

5. 輸出結果

出生日期格式不正確
必須爲false
年齡不正確

3. hibernate的校驗模式


1. 普通模式(默認爲該模式)

  • 會校驗所有屬性,然後返回所有的驗證失敗信息。

2. 快速失敗返回模式

  • 只要有一個校驗失敗則返回。

3. 設置方式

// failFast: true 快速失敗返回模式,false 普通模式
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
        .configure()
        .failFast( true )
        .buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
// hibernate.validator.fail_fast: true 快速失敗返回模式,false 普通模式
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
        .configure()
        .addProperty( "hibernate.validator.fail_fast", "true" )
        .buildValidatorFactory();
Validator validator = validatorFactory.getValidator();

4. hibernate的校驗


  • 配置hibernate Validator爲快速返回模式:

    @Configuration
    public class ValidatorConfiguration {
        @Bean
        public Validator validator(){
            ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                    .configure()
                    .addProperty( "hibernate.validator.fail_fast", "true" )
                    .buildValidatorFactory();
            Validator validator = validatorFactory.getValidator();
    
            return validator;
        }
    }
    

1. 請求參數校驗

  • 驗證請求參數時,在 @RequestBody DemoModel demo 之間加註解 @Valid,然後在後面加 BindindResult 即可;多個參數,可以添加多個 @Valid 和 BindindResult。
public void test()(@RequestBody @Valid DemoModel demo, BindingResult result)
public void test()(@RequestBody @Valid DemoModel demo, BindingResult result,@RequestBody @Valid DemoModel demo2, BindingResult result2)
@RequestMapping("/demo2")
public void demo2(@RequestBody @Valid DemoModel demo, BindingResult result){
    if(result.hasErrors()){
        for (ObjectError error : result.getAllErrors()) {
            System.out.println(error.getDefaultMessage());
        }
    }
}

2. GET參數校驗(@RequestParam參數校驗)

  1. controller

    @RequestMapping(value = "/demo3", method = RequestMethod.GET)
    public void demo3(@RequestParam(name = "grade", required = true) int grade,@RequestParam(name = "classroom", required = true) int classroom) {
        System.out.println(grade + "," + classroom);
    }
    
    • 使用 @Valid 註解對 RequestParam 對應的參數時無效的,需要使用 @Validated 註解來使驗證生效。
  2. MethodValidationPostProcessor 的 Bean

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        /**默認是普通模式,會返回所有的驗證不通過信息集合*/
        return new MethodValidationPostProcessor();
    }
    
  3. 或者可對 MethodValidationPostProcessor 進行設置 Validator

    • 此時不是使用 Validator 進行驗證,Validator 的配置不起作用
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
        /**設置validator模式爲快速失敗返回*/
        postProcessor.setValidator(validator());
        return postProcessor;
    }
    
    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                .configure()
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();
    
        return validator;
    }
    
  4. 方法所在的Controller上加註解 @Validated

    @RequestMapping("/validation")
    @RestController
    @Validated
    public class ValidationController {
        /**如果只有少數對象,直接把參數寫到Controller層,然後在Controller層進行驗證就可以了。*/
        @RequestMapping(value = "/demo3", method = RequestMethod.GET)
        public void demo3(@Range(min = 1, max = 9, message = "年級只能從1-9")
                          @RequestParam(name = "grade", required = true)
                          int grade,
                          @Min(value = 1, message = "班級最小隻能1")
                          @Max(value = 99, message = "班級最大隻能99")
                          @RequestParam(name = "classroom", required = true)
                          int classroom) {
            System.out.println(grade + "," + classroom);
        }
    }
    
  5. 返回驗證信息提示

    • 驗證不通過,拋出來 ConstraintViolationException 異常,使用統一捕獲異常處理
    @ControllerAdvice
    @Component
    public class GlobalExceptionHandler {
    
        @ExceptionHandler
        @ResponseBody
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        public String handle(ValidationException exception) {
            if(exception instanceof ConstraintViolationException){
                ConstraintViolationException exs = (ConstraintViolationException) exception;
    
                Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
                for (ConstraintViolation<?> item : violations) {
              /**打印驗證不通過的信息*/
                    System.out.println(item.getMessage());
                }
            }
            return "bad request, " ;
        }
    }
    
  6. 驗證

    http://localhost:8080/validation/demo3?grade=18&classroom=888
    

3. model校驗

  1. model

    @Data
    public class Demo2 {
        @Length(min = 5, max = 17, message = "length長度在[5,17]之間")
        private String length;
    
        /**@Size不能驗證Integer,適用於String, Collection, Map and arrays*/
        @Size(min = 1, max = 3, message = "size在[1,3]之間")
        private String age;
    
        @Range(min = 150, max = 250, message = "range在[150,250]之間")
        private int high;
    
        @Size(min = 3,max = 5,message = "list的Size在[3,5]")
        private List<String> list;
    }
    
  2. 校驗

    @Autowired
    private Validator validator;
    
    @RequestMapping("/demo3")
    public void demo3(){
        Demo2 demo2 = new Demo2();
        demo2.setAge("111");
        demo2.setHigh(150);
        demo2.setLength("ABCDE");
        demo2.setList(new ArrayList<String>(){{add("111");add("222");add("333");}});
        Set<ConstraintViolation<Demo2>> violationSet = validator.validate(demo2);
        for (ConstraintViolation<Demo2> model : violationSet) {
            System.out.println(model.getMessage());
        }
    }
    

4. 對象級聯校驗

  • 對象內部包含另一個對象作爲屬性,屬性上加 @Valid,可以驗證作爲屬性的對象內部的驗證
  1. demo

    @Data
    public class Demo2 {
        @Size(min = 3,max = 5,message = "list的Size在[3,5]")
        private List<String> list;
    
        @NotNull
        @Valid
        private Demo3 demo3;
    }
    
    @Data
    public class Demo3 {
        @Length(min = 5, max = 17, message = "length長度在[5,17]之間")
        private String extField;
    }
    
  2. 校驗

    /**前面配置了快速失敗返回的Bean*/
    @Autowired
    private Validator validator;
    
    @RequestMapping("/demo3")
    public void demo3(){
        Demo2 demo2 = new Demo2();
        demo2.setList(new ArrayList<String>(){{add("111");add("222");add("333");}});
    
        Demo3 demo3 = new Demo3();
        demo3.setExtField("22");
        demo2.setDemo3(demo3);
        Set<ConstraintViolation<Demo2>> violationSet = validator.validate(demo2);
        for (ConstraintViolation<Demo2> model : violationSet) {
            System.out.println(model.getMessage());
        }
    }
    

5. 分組校驗

  1. 校驗接口

    public interface GroupA {
    }
    
    public interface GroupB {
    }
    
  2. demo

    @Data
    public class Person {
        @NotBlank
        @Range(min = 1,max = Integer.MAX_VALUE,message = "必須大於0",groups = {GroupA.class})
        /**用戶id*/
        private Integer userId;
        @NotBlank
        @Length(min = 4,max = 20,message = "必須在[4,20]",groups = {GroupB.class})
        /**用戶名*/
        private String userName;
        @NotBlank
        @Range(min = 0,max = 100,message = "年齡必須在[0,100]",groups={Default.class})
        /**年齡*/
        private Integer age;
        @Range(min = 0,max = 2,message = "性別必須在[0,2]",groups = {GroupB.class})
        /**性別 0:未知;1:男;2:女*/
        private Integer sex;
    }
    
    • GroupA校驗字段userId
    • GroupB校驗字段userName、sex
    • Default校驗字段age(Default使Validator自帶的默認分組)
  3. 驗證

    • 只驗證GroupA和GroupB的分組,以下示例代碼
    @RequestMapping("/demo5")
    public void demo5(){
        Person p = new Person();
        /**GroupA驗證不通過*/
        p.setUserId(-12);
        /**GroupA驗證通過*/
        //p.setUserId(12);
        p.setUserName("a");
        p.setAge(110);
        p.setSex(5);
        Set<ConstraintViolation<Person>> validate = validator.validate(p, GroupA.class, GroupB.class);
        for (ConstraintViolation<Person> item : validate) {
            System.out.println(item);
        }
    }
    
    @RequestMapping("/demo6")
    public void demo6(@Validated({GroupA.class, GroupB.class}) Person p, BindingResult result){
        if(result.hasErrors()){
            List<ObjectError> allErrors = result.getAllErrors();
            for (ObjectError error : allErrors) {
                System.out.println(error);
            }
        }
    }
    
  4. 組序列

    • 指定組的驗證順序,前面組驗證不通過,後面組不驗證
    // GroupA > GroupB > Default
    @GroupSequence({GroupA.class, GroupB.class, Default.class})
    public interface GroupOrder {
    }
    

5. 自定義校驗器


1. 大小寫校驗器

public enum CaseMode {
    UPPER,
    LOWER;
}


@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckCaseValidator.class)
@Documented
public @interface CheckCase {
    String message() default "";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    CaseMode value();
}


public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> {
    private CaseMode caseMode;
    public void initialize(CheckCase checkCase) {
        this.caseMode = checkCase.value();
    }

    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        if (s == null) {
            return true;
        }

        if (caseMode == CaseMode.UPPER) {
            return s.equals(s.toUpperCase());
        } else {
            return s.equals(s.toLowerCase());
        }
    }
}

2. Demo

public class Demo{
    @CheckCase(value = CaseMode.LOWER,message = "userName必須是小寫")
    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

3. Validator配置

@Bean
public Validator validator(){
    ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
            .configure()
            .addProperty( "hibernate.validator.fail_fast", "true" )
            .buildValidatorFactory();
    Validator validator = validatorFactory.getValidator();

    return validator;
}

4. 校驗測試

@RequestMapping("/demo4")
public void demo4(){
    Demo demo = new Demo();
    demo.setUserName("userName");
    Set<ConstraintViolation<Demo>> validate = validator.validate(demo);
    for (ConstraintViolation<Demo> dem : validate) {
        System.out.println(dem.getMessage());
    }
}

6. 常見的註解


No.註解解釋
01@Null檢查該字段爲空
02@NotNull不能爲 null
03@NotBlank不能爲空,檢查時會將空格忽略
04@NotEmpty不能爲空,這裏的空是指空字符串
05@AssertTrue用於boolwan字段,只能爲true
06@AssertFalse用於boolwan字段,只能爲false
07@CreditCardNumber對信用卡進行一個大致的校驗
08@DecimalMin(value)數值類型,只能小於或等於value
09@DecimalMax(value)數值類型,只能大於或等於value
10@Digits(integer=2,fraction=20)限制必須爲一個小數,整數部分位數不能超過integer,小數部分位數不能超過fraction
11@Email檢查是否是一個有效的email地址
12@Past檢查該字段的日期是否屬於過去的日期
13@Future檢查該字段的日期是否屬於將來的日期
14@Length(min=,max=)檢查該字段的長度是否在min和max之間,只能用於字符串
15@Size(min=,max=)檢查該字段的size是否在min和max之間,可以是字符串、數組、集合、map等
16@Min(value)小於等於value
17@Max(value)大於等於value
18@URL(protocol=,host,port) 檢查是否是一個有效的URL,如果提供來protocol,host等,則該url還需滿足提供的條件
19@Valid該註解只要用於字段爲一個包含其他對象的集合或map或數組的字段,或該字段直接爲一個其他對象的引用(這樣在檢查當前對象的同時也會檢查該字段所引用的對象)

7. 參考文檔


參考文檔

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