Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.Sign up
Access JSR-303 validation contraint attributes in localized messages [SPR-6730] #11396
When using declarative validation constraints with Spring, custom/localized messages should be able to access the arguments to the validation annotation in a consistent manner that is guaranteed to work across validator providers.
For example, when applying an annotation like "
Currently, the values of the arguments passed to the annotation are available to the message source in an arguments array, but the position of each value in the array is not consistent. It appears that the positions of the arguments in the array can change depending on the validator provider and potentially change between releases of a validator provider. If the framework does currently provide some guarantee of the order, then this should be documented.
Here are some examples of validator annotations and the arguments array that is available to the message source when the validation fails. In all cases the first () element in the array is a DefaultMessageSourceResolvable containing the name of the field in error.
Affects: 3.0 GA
Referenced from: commits 7304c09
3 votes, 9 watchers
Stefan Larsson commented
The randomization happens in org.springframework.validation.beanvalidation.SpringValidatorAdapter, in the getArgumentsForConstraint() method:
arguments.add(new DefaultMessageSourceResolvable(codes, field));
The Map returned by getAttributes() is a HashMap, where the entry/value ordering is implementation-dependent. The ordering is even depending on if you are using SUN JDK version 5 or 6.
(I was planning to use Spring validation to perform validation and to localize the JSR-303/bean validation error messages, but that seems impossible with the current Spring implementation. Apparently using the plain javax.validation API and overriding javax.validation.MessageInterpolator to provide the user's locale as default is the way to go for now to localize a web application...)
Krzysztof Witukiewicz commented
I gave Spring a chance, and overwrote the method getArgumentsForConstraint in SpringValidatorAdapter (I needed support for groups, so I had to overwrite this class anyway) As was already mentioned, the problem is, that the order of message attributes can be non-deterministic. My idea was to generically enforce their order. It could be done in some well known properties file like this:
The format is simple:
The overwritten method returns an array, whose first element is the name of the field in error (like in the current implementation), and other elements are values of arguments in the specified order. Then, in your messages.properties, you can simply write:
What do you think about my solution? Is it an overkill? I know it is still only a workaround, and you have to initialize the adapter in your controllers and then call its validate method explicitly. I attach the files below.
Juergen Hoeller commented
I've addressed this in two different ways for Spring 3.0.4:
For the first option, Spring-managed field error arguments include the actual constraint annotation attributes in alphabetical order now. So for
As a second option, you may configure JSR-303's own message interpolation to operate against a Spring MessageSource: If you pass a MessageSource to LocalValidatorFactoryBean's "validationMessageSource" property (new in Spring 3.0.4), JSR-303's message keys will be resolved against that Spring-managed MessageSource and then interpolated by the validation provider - including named argument substition, as defined by JSR-303, but against messages from a Spring-managed resource bundle. That should be a powerful alternative to Spring field errors with numbered arguments.
Note that the second option requires Hibernate Validator 4.1 or above on the classpath. It'll work with other validation providers as well, but at least Hib Val 4.1's ResourceBundleMessageInterpolator class needs to be available.
Janning Vygen commented
The second option does not work if you use