How to conditionally call spring validators

I have a model as shown below. The existing code already has individual property checks. Now I have a requirement to ignore the existing checks on billing_country and address if buyer.method

is foo

. I thought I could have a custom validator at the level buyer

, validate the method, and only call validations when buyer.method!=foo

. Is this the correct approach? Are there any better alternatives?

"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"
           }
        }
      }
    ]
}

      

+3


source to share


2 answers


There are two ways to do this.

You can either

  • create a custom validation annotation and validator that validates the value method

    and then applies the appropriate validation to other fields

  • use validation groups where you manually validate the value method

    , then choose which group to use; your annotated fields then only need to be changed to apply when this group is active.

Option number 1

Define annotation at class level:

@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 {};
}

      

Define a validator:

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
        }
    }

}

      

Annotate customer class:

@ValidMethodAndInstruments
public class Buyer { }

      



Option number 2

Here you will need to manually check.

First, define the validation groups:

public interface FooValidation {}
public interface NotFooValidation {}

      

Configure the validator:

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

      

In the controller (?) Check the value method

and do the check:

@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);

      

Finally, change the groups in the model / dto class:

public class Buyer {
    // ...

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

      

+4


source


Answer

@beerbajay helped to some extent, so I'm going to accept his answer. However, I also wanted to write down a few of the problems we faced. We could not use the summary @Null

and @NotNull

the address, as it may be zero. Second, since we had to wrap this in a custom constraint validator, so NotFooValidation.class

we got an extra validation message line for that, which was not desirable. Finally, we were unable to use the groups @Valid

that were already present at Address

. We tried it @Validated

, but for some reason it didn't work. We completed the implementation ReaderInterceptor

to intercept the request and manually invoked the checks that were needed. Hope this helps someone along the way.



0


source







All Articles