如何有条件地调用spring验证器

时间:2015-06-16 19:47:32

标签: java spring validation

我有一个如下的模型。现有代码已经对各个属性进行了验证。现在,如果buyer.methodfoo,我需要忽略billing_country和地址的现有验证。我以为我可以在buyer级别拥有自定义验证器,检查方法并仅在buyer.method!=foo时调用验证。这是一种有效的方法吗?还有更好的选择吗?

"buyer": {
    "method": "foo",
    "instruments": [
      {
        "card": {
          "type": "MASTERCARD",
          "number": "234234234234234",
          "expire_month": "12",
          "expire_year": "2017",
          "billing_country" : "US"
          "Address" : {
             "line1": "summer st",
             "city": "Boston"
             "country": "US"
           }
        }
      }
    ]
}

2 个答案:

答案 0 :(得分:5)

有两种方法可以做到这一点。

你可以

  1. 创建自定义验证注释和验证器,用于检查method的值,然后将适用的验证应用于其他字段

  2. 使用validation groups,您可以手动检查method的值,然后选择要使用的组;然后,您的带注释的字段将需要更改为仅在该组处于活动状态时应用。

  3. 选项#1

    定义类级注释:

    @Target({ TYPE, ANNOTATION_TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = { MethodAndInstrumentsValidator.class })
    @Documented
    public @interface ValidMethodAndInstruments {
        String message() default "{my.package.MethodAndInstruments.message}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    

    定义验证器:

    public class MethodAndInstrumentsValidator 
                 implements ConstraintValidator<ValidMethodAndInstruments, Buyer> {
        public final void initialize(final ValidMethodAndInstruments annotation) {
            // setup
        }
    
        public final boolean isValid(final Buyer value, 
                                     final ConstraintValidatorContext context) {
            // validation logic
            if(!"foo".equals(value.getMethod())) {
                // do whatever conditional validation you want
            }
        }
    
    }
    

    注明买方类:

    @ValidMethodAndInstruments
    public class Buyer { }
    

    选项#2

    在这里,您必须手动调用验证。

    首先,定义验证组:

    public interface FooValidation {}
    public interface NotFooValidation {}
    

    配置验证器:

    @Bean
    public LocalValidatorFactoryBean validatorFactory() {
        return new LocalValidatorFactoryBean();
    }
    

    在你的控制器(?)中,检查method的值并进行验证:

    @Autowired
    private Validator validator;
    
    // ...
    
    public void validate(Object a, Class<?>... groups) {
        Set<ConstraintViolation<Object>> violations = validator.validate(a, groups);
        if(!violations.isEmpty()) {
            throw new ConstraintViolationException(violations);
        }
    }
    
    // ...
    
    validate(buyer, "foo".equals(buyer.getMethod()) 
                        ? FooValidation.class 
                        : NotFooValidation.class);
    

    最后,修改model / dto类中的组:

    public class Buyer {
        // ...
    
        @Null(groups = FooValidation.class)
        @NotNull(groups = NotFooValidation.class)
        protected String billingCountry;
    }
    

答案 1 :(得分:0)

@beerbajay答案在一定程度上有所帮助,所以我将接受他的回答。不过,我还想记下他们提出的几个问题。我们无法在地址上使用@Null@NotNull注释,因为它可能为null。其次,因为我们必须将它包装在自定义的Constraint验证器中,因此对于NotFooValidation.class我们得到了不需要的额外验证消息字符串。最后,我们无法使用@Valid中已存在的Address中的群组。我们尝试了@Validated,但由于某种原因这种做法并不奏效。我们最终实施ReaderInterceptor来拦截请求并手动调用所需的验证。希望这有助于有人在路上。