Spring MVC 使用介绍(十六)数据验证 (三)分组、自定义、跨参数、其他

一、概述

除了依赖注入、方法参数,Bean Validation 1.1定义的功能还包括:

1、分组验证

2、自定义验证规则

3、类级别验证

4、跨参数验证

5、组合多个验证注解

6、其他

二、分组验证

通过分组,可实现不同情况下的不同验证规则,示例如下:

1、定义分组接口

public interface AddView { 
}
public interface UpdateView {
}

2、定义实体

public class Person {
    @Null(groups = AddView.class)
    @NotNull(groups = {UpdateView.class, Default.class})
    private Integer id;
    ...
}

  注:不指定分组,即为Default分组

3、业务类

@Service
@Validated
public class PersonService {
    @Validated(AddView.class)
    public void addPerson(@Valid Person person) {}
    
    @Validated({UpdateView.class})
    public void updatePerson(@Valid Person person) {}
    
    public void defaultOp(@Valid Person person) {}
}

4、测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-context.xml")
public class ValidTest {
    @Autowired
    private PersonService testService;

    @Test(expected = ConstraintViolationException.class)
    public void test1() {
        Person person = new Person();
        person.setId(12);
        testService.addPerson(person);
    }
    
    @Test(expected = ConstraintViolationException.class)
    public void test2() {
        Person person = new Person();
        testService.updatePerson(person);
    }
    
    @Test(expected = ConstraintViolationException.class)
    public void test3() {
        Person person = new Person();
        testService.defaultOp(person);
    }
}

三、自定义验证规则

系统预定义的验证注解不能满足需求时,可自定义验证注解,示例如下:

1、自定义注解

@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = ForbiddenValidator.class)
@Documented
public @interface Forbidden {
    //默认错误消息
    String message() default "{forbidden.word}";

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

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

    //指定多个时使用
    @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        Forbidden[] value();
    }
}

2、自定义注解处理类

public class ForbiddenValidator implements ConstraintValidator<Forbidden, String> {
    private String[] forbiddenWords = {"admin"};

    @Override
    public void initialize(Forbidden constraintAnnotation) {
        //初始化,得到注解数据
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(StringUtils.isEmpty(value)) {
            return true;
        }

        for(String word : forbiddenWords) {
            if(value.contains(word)) {
                return false;//验证失败
            }
        }
        return true;
    }
}

3、默认错误消息

# format.properties
forbidden.word=包含敏感词汇

4、实体类

public class Person {
  @Forbidden
  private String name;
  ...
}

5、业务类

@Service
@Validated
public class PersonService {
    public void defaultOp(@Valid Person person) {
        
    }
}

6、测试

@Test(expected = ConstraintViolationException.class)
public void test4() {
    Person person = new Person();
    person.setName("admin");
    testService.defaultOp(person);
}

四、类级别验证

定义类级别验证,可实现对象中的多个属性组合验证,示例如下:

1、定义注解

@Target({ TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = CheckPasswordValidator.class)
public @interface CheckPassword {
    //默认错误消息
    String message() default "";

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

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

    //指定多个时使用
    @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        CheckPassword[] value();
    }
}

2、定义处理类

public class CheckPasswordValidator implements ConstraintValidator<CheckPassword, Person> {
    @Override
    public void initialize(CheckPassword constraintAnnotation) {
    }

    @Override
    public boolean isValid(Person person, ConstraintValidatorContext context) {
        if(person == null) {
            return true;
        }

        //没有填密码
        if(!StringUtils.isEmpty(person.getNewPassword())) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("{password.null}")
                    .addPropertyNode("password")
                    .addConstraintViolation();
            return false;
        }

        if(!StringUtils.isEmpty(person.getConfirmPassword())) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("{password.confirmation.null}")
                    .addPropertyNode("confirmation")
                    .addConstraintViolation();
            return false;
        }

        //两次密码不一样
        if (!person.getNewPassword().equals(person.getConfirmPassword())) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("{password.confirmation.error}")
                    .addPropertyNode("confirmation")
                    .addConstraintViolation();
            return false;
        }
        return true;
    }
}

3、实体

@CheckPassword
public class Person {
    private String newPassword;
    private String confirmPassword;
    ...
}

4、业务类

@Service
@Validated
public class PersonService {
    public void checkClassValidation(@Valid Person person) {
        
    }
}

5、测试

@Test(expected = ConstraintViolationException.class)
public void test4() {
    Person person = new Person();
    person.setNewPassword("asd");
    person.setConfirmPassword("12132");
    testService.checkClassValidation(person);
}

五、跨参数验证

使用跨参数验证,可实现方法级别中的多个参数组合验证,示例如下:

1、定义注解

@Constraint(validatedBy = CrossParameterValidator.class)
@Target({ METHOD, CONSTRUCTOR, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
public @interface CrossParameter {
    String message() default "{password.confirmation.error}";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}

2、定义处理类

@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class CrossParameterValidator implements ConstraintValidator<CrossParameter, Object[]> {
    @Override
    public void initialize(CrossParameter constraintAnnotation) {
    }

    @Override
    public boolean isValid(Object[] value, ConstraintValidatorContext context) {
        if(value == null || value.length != 2) {
            throw new IllegalArgumentException("must have two args");
        }
        if(value[0] == null || value[1] == null) {
            return true;
        }
        if(value[0].equals(value[1])) {
            return true;
        }
        return false;
    }
}

3、业务类

@Service
@Validated
public class PersonService {
    @CrossParameter
    public void checkParaValidation(String pw1, String pw2) {
        
    }
}

4、测试

@Test(expected = ConstraintViolationException.class)
public void test5() {
    testService.checkParaValidation("asd", "123");
}

六、组合多个验证注解

可将多个注解组合成一个注解,示例如下:

1、定义注解

@Target({ FIELD})
@Retention(RUNTIME)
@Documented
@NotNull
@Min(1)
@Constraint(validatedBy = { })
public @interface NotNullMin {
    String message() default "";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}

2、实体

public class Person {
    @NotNullMin
    private Integer id;
    ...
}

3、业务类

@Service
@Validated
public class PersonService {
    public void checkCompositionValidation(@Valid Person person) {
        
    }
}

4、测试

@Test(expected = ConstraintViolationException.class)
public void test6() {
    Person person = new Person();
    testService.checkCompositionValidation(person);
}

七、其他

Bean Validation 1.1还支持本地化、脚本验证器,详细见参考文档

参考:

Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC

原文地址:https://www.cnblogs.com/MattCheng/p/10697644.html