Skip to content

Spring MVC form validation

  • Bean validation features

  • required

  • length
  • numbers
  • regular expressions
  • custom validation

  • Hibernate validator

  • @InitBinder: Apply text pre-processing for request coming to the controller
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.1.6.Final</version>
</dependency>
public class Customer {
    private String firstName;

    @NotNull(message="must not be null")
    @Size(min=3, message="not long enough")
    private String lastName;

    @NotNull(message="is required")
    @Min(value=0, message="must be equal or greater than zero")
    @Max(value=10, message="must be equal or less than ten")
    private Integer freePasses; // if the type is 'int', initBinder will trim it to string 'null'

    @Pattern(regexp = "^[a-zA-Z0-9]{5}", message = "only 5 chars/digits")
    private String postalCode;
}
@RequestMapping("/processForm")
public String processForm(
        @Valid @ModelAttribute("customer") Customer theCustomer, // Populate theCustomer object coming from the request
        BindingResult theBindingResult) {// REsult of the validation. MUST come after the @valid

    System.out.printf("Last name: |" + theCustomer.getLastName()+ "|");
    System.out.println("Binding result: " + theBindingResult);
    System.out.println("\n\n\n\n");

    if (theBindingResult.hasErrors()) {
        return "customer-form";
    } else {
        return "customer-confirmation";
    }
}

Custom validation rule

  • Create a new annotation
@Constraint(validatedBy = CourseCodeConstraintValidator.class) // Class with the business logic for the validator
@Target( {ElementType.METHOD, ElementType.FIELD} ) // Where to apply this annotation: Methods and Fields
@Retention(RetentionPolicy.RUNTIME) // How long the annotation will be stored/used. Retain at source code and use at runtime by the JVM
public @interface CourseCode {
    // default value
    public String[] value() default {"LUV"}; // Validate against an array of strings

    // default message (for errors)
    public String message() default "must start with LUV";

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

    // default payloads
    public Class<? extends Payload>[] payload() default {};
}
  • CourseCodeConstraintValidator.class
public class CourseCodeConstraintValidator implements ConstraintValidator<CourseCode, String> { // ConstraintValidator is a interface from javax.validation pkg

    private String[] coursePrefixes;

    @Override
    public void initialize(CourseCode theCourseCode) {
        coursePrefixes = theCourseCode.value(); // The value of the annotation
    }

    @Override
    public boolean isValid(String theCode, ConstraintValidatorContext theConstraintValidatorContext) {
        boolean result = false; // do not validate by default



        if (theCode != null) {
            //
            // loop thru course prefixes
            //
            // check to see if code matches any of the course prefixes
            //
            for (String tempPrefix : coursePrefixes) {
                result = theCode.startsWith(tempPrefix);

                // if we found a match then break out of the loop
                if (result) break;
            }

        } else {
            result = true; // If nothing is passed, just pass it (nothing to validate)
        }

        return result;
    }
}
@CourseCode // @CourseCode(value={"TOPS", "LUV"}, message="must start with LUV") is the default
private String courseCode;