Given a username
field which cannot be null
, must be between 4 and 64 characters long and match the regexp [A-Za-z0-9]
, when the username
field is null, the error message is simply: must not be null
. The desired outcome is must not be null AND length must be between 4 and 64 characters AND must match "[A-Za-z0-9] "
.
Initial setup:
@NotNull
@Length(min = 4, max = 64)
@Pattern(regexp = "[A-Za-z0-9] ")
String username;
What I also tried:
@NotNull(message = """
{jakarta.validation.constraints.NotNull.message}
AND {org.hibernate.validator.constraints.Length.message}
AND {jakarta.validation.constraints.Pattern.message}""")
@Length(min = 4, max = 64)
@Pattern(regexp = "[A-Za-z0-9] ")
String username;
But this latter's outcome is:
ConstraintViolationImpl{
interpolatedMessage='must not be null AND length must be between {min} and {max} AND must match "{regexp}"',
propertyPath=username, rootBeanClass=class app.User,
messageTemplate='{jakarta.validation.constraints.NotNull.message} AND {org.hibernate.validator.constraints.Length.message} AND {jakarta.validation.constraints.Pattern.message}'}
The values for the constraints (min
, max
, regexp
) are not accessed. How to render the actual values of these in the error message?
CodePudding user response:
Most constraint annotations, including @Length
and @Pattern
, regard null
as valid input. That's why you won't get what you want by just using these annotations.
Fortunately, it's really easy to do what you want by introducing a new constraint annotation:
@Constraint(validatedBy = {}) // no validator needed, it delegates to other constraints
@NotNull
@Length(min = 4, max = 64)
@Pattern(regexp = "[A-Za-z0-9] ")
@ReportAsSingleViolation // to prevent separate violations for the other constraints
@Target(FIELD)
@Retention(RUNTIME)
public @interface ValidUsername {
String message() default """
{jakarta.validation.constraints.NotNull.message}
AND {org.hibernate.validator.constraints.Length.message}
AND {jakarta.validation.constraints.Pattern.message}""";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
This is annotated with your constraints, which means these are applied when this annotation is checked.
The message will still contain place holders {min}
etc., because these are not properties of this constraint itself. You can solve that in two ways:
- Don't use the templates but hard-code the message
- Add these properties to your annotation as well, and tell the Bean Validation framework to use these for the other annotations:
@OverridesAttribute(constraint = Length.class)
int min() default 4;
@OverridesAttribute(constraint = Length.class)
int max() default 64;
@OverridesAttribute(constraint = Pattern.class)
String regexp() default "[A-Za-z0-9] ";
Now all you need to do is replace the annotations on the username
field with @ValidUsername
.