I've written a validation annotation implemented by a custom ConstraintValidator
. I also want to generate very specific ConstraintViolation
objects that use values computed during the validation process during message interpolation.
public class CustomValidator
implements ConstraintValidator<CustomAnnotation, ValidatedType> {
...
@Override
public boolean isValid(ValidatedType value, ConstraintValidatorContext context) {
// Figure out that the value is not valid.
// Now, I want to add a violation whose error message requires arguments.
}
}
A hypothetical error message in my message source:
CustomAnnotation.notValid = The supplied value {value} was not valid because {reason}.
The context passed into the isValid
method provides an interface for building up a constraint violation, and finally adding it to the context. However, I can't seem to figure out how to use it. According to this documention for the version I'm using, I can add bean and property nodes to the violation. These are the only additional details I can specify to the violation definition, but I don't understand how they might map to parameters in the error message.
Million dollar question: how can I pass dynamic parameters to my validation error messages using a custom validator? I would like to fill in those {value}
and {reason}
fields using the ConstraintValidatorContext
's interface for building violations.
Obtaining an instance of the message source and interpolating the message within the custom validator is not an option - the messages coming out of validation get interpolated no matter what, and interpolating internally will result in some messages being interpolated twice, potentially annihilating escaped single quotes or other characters with special meanings in my message definitions file.
That's not possible with the standardized Bean Valiation API, but there is a way in Hibernate Validator, the BV reference implementation.
You need to unwrap the ConstraintValidatorContext
into a HibernateConstraintValidatorContext
which gives you access to the addExpressionVariable()
method:
public class MyFutureValidator implements ConstraintValidator<Future, Date> {
public void initialize(Future constraintAnnotation) {}
public boolean isValid(Date value, ConstraintValidatorContext context) {
Date now = GregorianCalendar.getInstance().getTime();
if ( value.before( now ) ) {
HibernateConstraintValidatorContext hibernateContext =
context.unwrap( HibernateConstraintValidatorContext.class );
hibernateContext.disableDefaultConstraintViolation();
hibernateContext.addExpressionVariable( "now", now )
.buildConstraintViolationWithTemplate( "Must be after ${now}" )
.addConstraintViolation();
return false;
}
return true;
}
}
The reference guide has some more details.
If you use message codes you can simply add something like {0} in them.
For example: "The field {0} must not be empty."
And then use hibernateContext.addMessageParameter("0", fieldName);
instead of addExpressionVariable(...)
.
That worked for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With