JSR303验证 - 使用自定义类验证器中的组

时间:2022-06-01 17:03:15

We have a use case in which we have a fairly poorly constructed bean that contains fields like so:

我们有一个用例,其中我们有一个相当糟糕的bean,包含如下字段:

public class DataBean {
    private boolean flag1;
    private boolean flag2;

    private String phone1;
    private String address1;
    private String city1;
    private String state1;

    private String phone2;
    private String address2;
    private String city2;
    private String state2;
}

We need to validate phone/address/city/state [1|2] only if flag [1|2] is true. Bad, bad design, granted.

只有当标志[1 | 2]为真时,我们才需要验证电话/地址/城市/州[1 | 2]。糟糕,糟糕的设计,被授予。

Our current strategy is to use @NotNull (or whatever validations we need) on each of the "real" data fields, and use a groups indicator, like so:

我们当前的策略是在每个“真实”数据字段上使用@NotNull(或我们需要的任何验证),并使用组指示符,如下所示:

public class DataBean {
    private boolean flag1;
    private boolean flag2;

    @NotNull(groups = Info.First.class)
    private String phone1;
    @NotNull(groups = Info.First.class)
    private String address1;
    @NotNull(groups = Info.First.class)
    private String city1;
    @NotNull(groups = Info.First.class)
    private String state1;

    @NotNull(groups = Info.Second.class)
    private String phone2;
    @NotNull(groups = Info.Second.class)
    private String address2;
    @NotNull(groups = Info.Second.class)
    private String city2;
    @NotNull(groups = Info.Second.class)
    private String state2;
}

In our business logic where we are validating this bean (which has various other fields in it that will be validated by the "default" validation group), we will get violations for the "default" group, then check if flag1 is true, if so, run validations for Info.First.class, check if flag2 is true, then run validations for Info.Second.class.

在我们验证此bean的业务逻辑中(其中包含将由“默认”验证组验证的各种其他字段),我们将违反“默认”组,然后检查flag1是否为true,如果因此,运行Info.First.class的验证,检查flag2是否为true,然后运行Info.Second.class的验证。

Now the question ... is there any way of hooking into these groups from a custom class validator? I'm envisioning having a class validator that takes the flag1/flag2 properties and their corresponding custom groups and when isValid is invoked, it does these secondary/tertiary calls for the groups. The purpose being, simply, that the custom class validator would be in the default group, so the business logic validating this class wouldn't have the details of this ugly legacy design leak into it by virtue of having to call the validation separately.

现在问题是......有没有办法从自定义类验证器挂钩这些组?我设想有一个类验证器,它接受flag1 / flag2属性及其相应的自定义组,并且当调用isValid时,它会对这些组执行这些二级/三级调用。简单来说,目的是将自定义类验证器放在默认组中,因此验证此类的业务逻辑不会因为必须单独调用验证而将此丑陋的遗留设计的详细信息泄漏到其中。

Thoughts? Thanks!

2 个解决方案

#1


0  

I'm not entirely certain I've grasped what problem you are trying to solve. My interpretation is that are trying to avoid explicitly making separate calls for each validation group but call the two non default groups according to the flags? Can not just define some group sequences and use these instead of the flags? Group sequences are used in the same way as groups. The only thing about them is that they will stop validating the groups if one group fails.

我不完全确定我已经掌握了你想要解决的问题。我的解释是,试图避免为每个验证组明确地单独调用,但根据标志调用两个非默认组?不能只定义一些组序列并使用这些而不是标志?组序列的使用方式与组相同。关于他们的唯一事情是,如果一个组失败,他们将停止验证组。

If you need to validate always validate all of the groups according to the flags, the your custom class validator can call groups() on the constraint annotation passed into its initialize method.

如果需要验证始终根据标志验证所有组,则自定义类验证器可以对传递给其initialize方法的约束注释调用groups()。

#2


0  

Make a class level validator and initialize the validator inside it. You can then validate the object by groups inside the main valid method of the class level constraint by adding constraint exceptions on each field. See below:

创建一个类级别验证器并初始化其中的验证器。然后,您可以通过在每个字段上添加约束异常,按类级别约束的主有效方法内的组验证对象。见下文:

Constraint interface:

@Documented
@Constraint(validatedBy = {DataBeanValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, TYPE})
@Retention(RUNTIME)
public @interface DataBeanConstraint {

    String message() default "validation.dataBean";

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

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

Validator:

public class DataBeanValidator implements ConstraintValidator<DataBeanConstraint, DataBean> {

    private Validator validator;

    @Override
    public void initialize(DataBeanConstraint constraintAnnotation) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    @Override
    public boolean isValid(BeanData beanData, ConstraintValidatorContext context) {
        if (beanData == null) {
            return true;
        }
        if (beanData.isFlag1) {
            Set<ConstraintViolation<DataBean>> constraintViolations = validator.validate(beanData, Info.First.class);
            if (constraintViolations != null) {
                for (ConstraintViolation<BeanData> constraintViolation : constraintViolations) {
                    context.disableDefaultConstraintViolation();
                    context.buildConstraintViolationWithTemplate("required field").
                            addNode(constraintViolation.getPropertyPath().toString())
                            .addConstraintViolation();
                }
            }
        }

    }
}

Class level validator:

班级验证员:

@DataBeanConstraint
public class DataBean {
    private boolean flag1;
    private boolean flag2;

    @NotNull(groups = Info.First.class)
    private String phone1;
    @NotNull(groups = Info.First.class)
    private String address1;
    @NotNull(groups = Info.First.class)
    private String city1;
    @NotNull(groups = Info.First.class)
    private String state1;

    @NotNull(groups = Info.Second.class)
    private String phone2;
    @NotNull(groups = Info.Second.class)
    private String address2;
    @NotNull(groups = Info.Second.class)
    private String city2;
    @NotNull(groups = Info.Second.class)
    private String state2;
}

#1


0  

I'm not entirely certain I've grasped what problem you are trying to solve. My interpretation is that are trying to avoid explicitly making separate calls for each validation group but call the two non default groups according to the flags? Can not just define some group sequences and use these instead of the flags? Group sequences are used in the same way as groups. The only thing about them is that they will stop validating the groups if one group fails.

我不完全确定我已经掌握了你想要解决的问题。我的解释是,试图避免为每个验证组明确地单独调用,但根据标志调用两个非默认组?不能只定义一些组序列并使用这些而不是标志?组序列的使用方式与组相同。关于他们的唯一事情是,如果一个组失败,他们将停止验证组。

If you need to validate always validate all of the groups according to the flags, the your custom class validator can call groups() on the constraint annotation passed into its initialize method.

如果需要验证始终根据标志验证所有组,则自定义类验证器可以对传递给其initialize方法的约束注释调用groups()。

#2


0  

Make a class level validator and initialize the validator inside it. You can then validate the object by groups inside the main valid method of the class level constraint by adding constraint exceptions on each field. See below:

创建一个类级别验证器并初始化其中的验证器。然后,您可以通过在每个字段上添加约束异常,按类级别约束的主有效方法内的组验证对象。见下文:

Constraint interface:

@Documented
@Constraint(validatedBy = {DataBeanValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, TYPE})
@Retention(RUNTIME)
public @interface DataBeanConstraint {

    String message() default "validation.dataBean";

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

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

Validator:

public class DataBeanValidator implements ConstraintValidator<DataBeanConstraint, DataBean> {

    private Validator validator;

    @Override
    public void initialize(DataBeanConstraint constraintAnnotation) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    @Override
    public boolean isValid(BeanData beanData, ConstraintValidatorContext context) {
        if (beanData == null) {
            return true;
        }
        if (beanData.isFlag1) {
            Set<ConstraintViolation<DataBean>> constraintViolations = validator.validate(beanData, Info.First.class);
            if (constraintViolations != null) {
                for (ConstraintViolation<BeanData> constraintViolation : constraintViolations) {
                    context.disableDefaultConstraintViolation();
                    context.buildConstraintViolationWithTemplate("required field").
                            addNode(constraintViolation.getPropertyPath().toString())
                            .addConstraintViolation();
                }
            }
        }

    }
}

Class level validator:

班级验证员:

@DataBeanConstraint
public class DataBean {
    private boolean flag1;
    private boolean flag2;

    @NotNull(groups = Info.First.class)
    private String phone1;
    @NotNull(groups = Info.First.class)
    private String address1;
    @NotNull(groups = Info.First.class)
    private String city1;
    @NotNull(groups = Info.First.class)
    private String state1;

    @NotNull(groups = Info.Second.class)
    private String phone2;
    @NotNull(groups = Info.Second.class)
    private String address2;
    @NotNull(groups = Info.Second.class)
    private String city2;
    @NotNull(groups = Info.Second.class)
    private String state2;
}