New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Access JSR-303 validation contraint attributes in localized messages [SPR-6730] #11396

Closed
spring-issuemaster opened this Issue Jan 20, 2010 · 6 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Collaborator

spring-issuemaster commented Jan 20, 2010

Scott Frederick opened SPR-6730 and commented

Related to: #11073 and #10092.

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 "@Size(min=1, max=25)" to a field you should be able to provide an error message format like "Size.fieldName=Size of {0} must be between {1} and {2}".

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 ([0]) element in the array is a DefaultMessageSourceResolvable containing the name of the field in error.

@Size(min=1, max=25)

index contents
1 "message" argument from annotation
2 "min" argument from annotation
3 "payload" argument from annotation
4 "max" attribute of annotation
5 "groups" attribute of annotation

@Max(25)

index contents
1 "value" argument from annotation
2 "message" argument from annotation
3 "groups" attribute of annotation
4 "payload" argument from annotation

@Min(1)

index contents
1 "value" argument from annotation
2 "message" argument from annotation
3 "groups" attribute of annotation
4 "payload" argument from annotation

@Pattern(regexp="[A-Z]+")

index contents
1 "message" argument from annotation
2 "payload" argument from annotation
3 "flags" argument from annotation
4 "regexp" attribute of annotation
5 "groups" attribute of annotation

Affects: 3.0 GA

Reference URL: http://forum.springsource.org/showthread.php?p=279199#post279199

Attachments:

Issue Links:

  • #11966 Expose MessageSource through Hibernate Validator 4.1's ResourceBundleLocator

Referenced from: commits 7304c09

3 votes, 9 watchers

@spring-issuemaster

This comment has been minimized.

Collaborator

spring-issuemaster commented Jun 21, 2010

Stefan Larsson commented

The randomization happens in org.springframework.validation.beanvalidation.SpringValidatorAdapter, in the getArgumentsForConstraint() method:

arguments.add(new DefaultMessageSourceResolvable(codes, field));
arguments.addAll(descriptor.getAttributes().values());

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...)

@spring-issuemaster

This comment has been minimized.

Collaborator

spring-issuemaster commented Jun 23, 2010

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:

javax.validation.constraints.Size=min:max
javax.validation.constraints.Future=
javax.validation.constraints.Max=value

The format is simple:

<class name of the constraint>=<argument names>
<argument names> ::= [<argument name>{:<argument name>}]

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:

Size.java.lang.String=Length of {0} has to be between {1} and {2}

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.

@spring-issuemaster

This comment has been minimized.

Collaborator

spring-issuemaster commented Jun 23, 2010

Krzysztof Witukiewicz commented

The adapter with overwritten method getArgumentsForConstraint

@spring-issuemaster

This comment has been minimized.

Collaborator

spring-issuemaster commented Jun 23, 2010

Krzysztof Witukiewicz commented

Class that read the properties file with constraints' arguments' order

@spring-issuemaster

This comment has been minimized.

Collaborator

spring-issuemaster commented Jul 21, 2010

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 @Size(min=1,max=5), it'll include the field name as {0}, the max value as {1}, and the min value as {2}. There is no way to determine the actual declaration order in the annotation definition, so alphabetical is the best we can do here - as well as suppressing JSR-303's 'internal' attributes ("message", "groups", "payload"). External ordering metadata would be a bit overkill here, IMO, in particular given the other option below...

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.

Juergen

@spring-issuemaster

This comment has been minimized.

Collaborator

spring-issuemaster commented Jul 31, 2014

Janning Vygen commented

The second option does not work if you use messageSource.setAlwaysUseMessageFormat(true). We need this option because our translators need a consistent format (easier then to say "double every apostrophe only if your find things like {0}"). If the message contains {min} and {max} Spring tries to interpolate it and fails with

Caused by: java.lang.IllegalArgumentException: can't parse argument number: min

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment