0

I created 2 custom validators.

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = ISODateValidator.class)
public @interface ISODateConstraint {
    String message() default "Field is not a valid iso date";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
public class ISODateValidator implements ConstraintValidator<ISODateConstraint, String> {

    @Override
    public void initialize(ISODateConstraint constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        try {
            LocalDate.parse(s);
        } catch (DateTimeParseException e) {
            return false;
        }
        return true;
    }
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = AgeValidator.class)
public @interface AgeConstraint {
    String message() default "Age is not in required range";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    int min();
    int max();
}
public class AgeValidator implements ConstraintValidator<AgeConstraint, String> {

    private int min;
    private int max;
    @Override
    public void initialize(AgeConstraint constraintAnnotation) {
        this.min = constraintAnnotation.min();
        this.max = constraintAnnotation.max();
    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        LocalDate date;
        try {
             date = LocalDate.parse(s);
        } catch (DateTimeParseException e) {
            return false;
        }
        return ( LocalDate.now().minusYears(min).isAfter(date) || LocalDate.now().minusYears(min).isEqual(date))
               && ( LocalDate.now().minusYears(max).isBefore(date) || LocalDate.now().minusYears(max).isEqual(date));
    }
}

And used them on field

@AgeConstraint(min = 13, max = 100)
@ISODateConstraint
private String dateOfBirth;

I care about validation message so order of execution is relevant. Now when i provide string like "2022-13-12" validation should fail on ISODateConstraint and return related message. What i get is message related to AgeConstraint.

I checked execution with debugger and ISODateValidator is always executing first and always returns false. Despite this execution goes further into ISODateValidator.

kuna7
  • 1
  • You shouldn't be checking the format with a validator, it should just fail in binding. You shouldn't use a `String` to store your date but a `LocalDate`. That way binding will fail. There are things you should use validation for and there are things that aren't. The format you shouldn't validate in this case. – M. Deinum Dec 05 '22 at 06:37
  • In slight opposition to @M.Deinum, I do recommend making all values coming in from your Command object to be of type String. I feel that it is easier to handle/manage the error messages that are returned to the end users and in the order that you prefer. I then have methods on the Command that are used to cast the values to the proper type. Like `getAgeAsInt()`. – hooknc Dec 07 '22 at 16:37

1 Answers1

0

This is not totally what you asked, but the answer I wrote for a different validation question (How to perform custom validations in Spring MVC?) should work.

I would look into the @GroupSequence annotation.

Baeldung has a decent tutorial, Grouping Javax Validation Constraints.

hooknc
  • 4,854
  • 5
  • 31
  • 60