SpringFramework特性(二)校验注解的使用

  • A+

SpringFramework是SpringBoot的基石,所以在SpringBoot中使用validator进行验证,实际使用的是SpringFramework中的bean验证特性。而在SpringFramework中集成的是实现了JSR-303标准的Hibernate验证框架,所以我们使用的大多数与验证相关的注解都是Hibernate验证框架实现的。

1.基础类型及其包装类校验

  • 在类上使用@Validated,方法的形参和返回参数上的约束将会生效
    @RestController
    @Validated
    public class ValidationTestWebBeanController {
    
        /**
         * 基础类型以及其包装类约束
         * @param hair
         * @return
         */
        @PostMapping("/testValidatedAnnotation")
        public @Length(min = 1,max =10 ) String testValidatedAnnotation(  @RequestParam @Range(min = 20,max = 1000000) Long hair){
            return  "joey:"+hair;
        }
    }
    
  • 测试方法如下,可见传入了hair数量低于最小量,抛出了一个预期内的错误
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @AutoConfigureMockMvc
    public class ValidationTestWebBeanControllerTest {
        @Autowired
        private MockMvc mockMvc;
        @Test
        public void testValidatedAnnotation() {
            try {
                mockMvc.perform(post("/testValidatedAnnotation").param("hair", "10").contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE));
            } catch (Exception e) {
                Assert.assertThat(e.getMessage(), containsString("ConstraintViolationException"));
            }
    
        }
    }
    

2.集合类的约束

  • 对集合进行校验和常规校验相同:
    @RestController
    @Validated
    public class ValidationTestWebBeanController {
       /**
         * 集合约束
         * @param words
         * @return
         */
        @PostMapping("/testValidatedCollection")
        public  String testValidatedCollection(@RequestBody @Size(min = 2) @NotEmpty List< @Length(max = 4) String>  words){
            return StringUtils.join(words);
        }
    }
    
  • 测试方法如下,测试上例约束,集合不为空,若有元素则至少2个,集合内部字符串的元素最长4个字符:
     @Test
        public void testValidatedCollection() {
            try {
                List<String> words = new ArrayList<>();
                words.add("I");
                words.add("am");
                words.add("Joseph Francis Tribbiani");
                mockMvc.perform(post("/testValidatedCollection").content(new ObjectMapper().writeValueAsBytes(words)).contentType(MediaType.APPLICATION_JSON));
            } catch (Exception e) {
                Assert.assertThat(e.getMessage(), containsString("ConstraintViolationException"));
            }
        }
    

3.@Valid注解的使用一,校验常规bean中约束

  • @Valid注解使用在非原始字段或方法上;大多使用于自定义bean中,为了触发bean内部的常规校验,见如下范例:
    @RestController
    @Validated
    public class ValidationTestWebBeanController {
        /**
         * 自定义包装类约束
         * @param validationTestBean
         * @return
         */
        @PostMapping("/testValidatedBeanProperties")
        public  ValidationTestBean testValidatedBeanProperties(@Valid ValidationTestBean validationTestBean ){
            return  validationTestBean;
        }
        /**
         * 集合中包含包装类
         * @param validationTestBeans
         * @return
         */
        @PostMapping("/testValidatedBeanPropertiesCollection")
        public  Integer testValidatedBeanPropertiesCollection(@RequestBody @NotEmpty List<@Valid ValidationTestBean> validationTestBeans ){
            return  validationTestBeans.size();
        }
    
        @Data
        static class ValidationTestBean {
            @Length(min = 1,max = 8)
            @NotNull
            private String firstName;
    
            @NotBlank(message = "不能为空或者\"\"")
            private String lastName;
        }
    }
    
  • 测试方法如下,@Valid放置在bean前,bean中定义的约束注解将生效:
       @Test
        public void testValidatedBeanProperties() throws Exception {
                mockMvc.perform(post("/testValidatedBeanProperties").param("firstName","Joey").contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)).andExpect(status().is(400));
        }
         @Test
        public void testValidatedBeanPropertiesCollection() {
            try {
                List<ValidationTestWebBeanController.ValidationTestBean> validationTestBeans = new ArrayList<>();
                ValidationTestWebBeanController.ValidationTestBean validationTestBean = new ValidationTestWebBeanController.ValidationTestBean();
                validationTestBean.setFirstName("joey");
                validationTestBeans.add(validationTestBean);
                mockMvc.perform(post("/testValidatedBeanPropertiesCollection").content(new ObjectMapper().writeValueAsBytes(validationTestBeans)).contentType(MediaType.APPLICATION_JSON));
            } catch (Exception e) {
                Assert.assertThat(e.getMessage(), containsString("ConstraintViolationException"));
            }
        }
    

4.@Valid注解的使用二,级联验证

  • bean进行验证的时候很多都是嵌套对象,我们需要使用@Valid来进行标记,激活嵌套对象中的约束属性,见如下范例:

    /** * 对象的级联验证 * @param validationTestBeanCascaded * @return */ @PostMapping("/testValidationTestBeanCascaded") public ValidationTestBeanCascaded testValidationTestBeanCascaded(@RequestBody @Valid ValidationTestBeanCascaded validationTestBeanCascaded ){ return validationTestBeanCascaded; } @Data static class ValidationTestBean { @Length(min = 1,max = 8) @NotNull private String firstName; @NotBlank(message = "不能为空或者\"\"") private String lastName; } @Data static class ValidationTestBeanCascaded{ @AssertTrue private Boolean isMan; @Valid private ValidationTestBean validationTestBean; }
  • 测试方法如下
     @Test
        public void testValidationTestBeanCascaded() throws Exception {
                ValidationTestWebBeanController.ValidationTestBeanCascaded validationTestBeanCascaded = new ValidationTestWebBeanController.ValidationTestBeanCascaded();
                validationTestBeanCascaded.setIsMan(true);
                ValidationTestWebBeanController.ValidationTestBean validationTestBean = new ValidationTestWebBeanController.ValidationTestBean();
                validationTestBean.setFirstName("joey");
                validationTestBeanCascaded.setValidationTestBean(validationTestBean);
                mockMvc.perform(post("/testValidationTestBeanCascaded").content(new ObjectMapper().writeValueAsBytes(validationTestBeanCascaded)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().is4xxClientError());
        }
    

5.分组验证

  • 在bean验证中我们有这样的需求,不同的方法中验证不同的参数,这时我们需要①@Validated的value值标明需要校验的分组;②每一个常规约束注解中都有groups参数,指定归属分组,范例如下:
      @PostMapping("/testValidationTestBeanGroup")
        public  ValidationTestBeanGroup testValidationTestBeanGroup(@RequestBody  @Validated(value = ValidationTestBeanGroup.Insert.class) ValidationTestBeanGroup validationTestBeanGroup ){
            return  validationTestBeanGroup;
        }
    
        @PostMapping("/testValidationTestBeanGroups")
        public  ValidationTestBeanGroup testValidationTestBeanGroups(@RequestBody  @Validated(value = {ValidationTestBeanGroup.Insert.class, ValidationTestBeanGroup.Default.class}) ValidationTestBeanGroup validationTestBeanGroup ){
            return  validationTestBeanGroup;
        }
    
        @Data
        static class ValidationTestBeanGroup {
    
            public  interface  Insert{}
            public  interface  Default{}
            @NotNull(groups = Insert.class)
            private Integer id;
    
            @NotNull(groups = Default.class)
            private String firstName;
    
            @NotBlank(message = "不能为空或者\"\"" )
            private String lastName;
        }
    
  • 上面范例可知,@Validated(value = ValidationTestBeanGroup.Insert.class)只会校验包含Insert接口的注解,@Validated(value = {ValidationTestBeanGroup.Insert.class, ValidationTestBeanGroup.Default.class}校验包含Insert和Default接口类的注解
       @Test
        public void testValidationTestBeanGroup() throws Exception {
            ValidationTestWebBeanController.ValidationTestBeanGroup validationTestBeanGroup = new ValidationTestWebBeanController.ValidationTestBeanGroup();
            validationTestBeanGroup.setId(100);
            validationTestBeanGroup.setFirstName("joey");
            mockMvc.perform(post("/testValidationTestBeanGroup").content(new ObjectMapper().writeValueAsBytes(validationTestBeanGroup)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk());
        }
    

6.总结

zhangfeng

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: