0

I have a Spring Boot web app in which fields of my form-backing bean are annotated with Bean Validation annotations (see Baeldung's tutorial or the docs at spring.io). For example, fields on Customer beans might be annotated like this:

@NotBlank
@Pattern(regexp="[ \\.A-Za-z-]*")
private String firstName;

@DateTimeFormat(pattern="M/d/yyyy")
@NotNull
@Past
private Date DOB;

What I want to know is: (How) Can I implement a complex validation that looks at multiple fields? I mean, using this framework. For example, let's say I have a field Country and a field ZipCode and I want the ZipCode to be @NotBlank if and only if the Country equals "US", optional otherwise.

In my Controller, the validation is very elegantly triggered with the use of the @Valid annotation, and errors are attached to a BindingResult object. Like so:

@PostMapping("customer/{id}")
public String updateCustomer( @PathVariable("id") Integer id,
                              @Valid @ModelAttribute("form") CustomerForm form, 
                              BindingResult bindingResult ) {
    if (bindingResult.hasErrors()) {
        return "customerview";
    }
    customerService.updateCustomer(form);
    return "redirect:/customer/"+id;
}

What I'm hoping to find is a way to write this conditional validator in a way that it, too, will be triggered by the @Valid annotation and will attach it's error message to the ZipCode field in the BindingResult. Ideally I shouldn't have to change the Controller code at all.

workerjoe
  • 2,421
  • 1
  • 26
  • 49

2 Answers2

1

you can getting help of creating custom validator class, the following link helps you: https://www.baeldung.com/spring-mvc-custom-validator#custom-validation

1

For this kind of stuff you need to use cross field validation. Please try :

Create annotation :

package foo.bar;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {CustomerValidator.class})
public @interface CustomerValid {

    String message() default "{foo.bar.CustomerValid.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

}

Create custom validator :

public class CustomerValidator implements ConstraintValidator<CustomerValid, Customer> {

    @Override
    public void initialize(CustomerValid constraint) {
    }

    @Override
    public boolean isValid(Customer customer, ConstraintValidatorContext context) {
        return !("US".equals(customer.getCountry() && "".equals(customer.getZipCode())));
    }
}

Annotate your class :

@CustomerValid
public class Customer {
// body
}

This class validation will be processed in addition to existent field validators.

zpavel
  • 951
  • 5
  • 11