-
Notifications
You must be signed in to change notification settings - Fork 38.8k
Description
Bertrand Guay-Paquet opened SPR-14104 and commented
When using JSR-303 annotations for validation, Spring looks up error messages in message bundles and makes available the field name as well as the annotation attribute values in alphabetical order. These arguments are determined by LocalValidatorFactoryBean via its superclass SpringValidatorAdapter with this cose:
protected Object[] getArgumentsForConstraint(String objectName, String field, ConstraintDescriptor<?> descriptor) {
List<Object> arguments = new LinkedList<Object>();
// HERE are established the message keys for resolving the field name
String[] codes = new String[] {objectName + Errors.NESTED_PATH_SEPARATOR + field, field};
arguments.add(new DefaultMessageSourceResolvable(codes, field));
// Using a TreeMap for alphabetical ordering of attribute names
Map<String, Object> attributesToExpose = new TreeMap<String, Object>();
for (Map.Entry<String, Object> entry : descriptor.getAttributes().entrySet()) {
String attributeName = entry.getKey();
Object attributeValue = entry.getValue();
if (!internalAnnotationAttributes.contains(attributeName)) {
attributesToExpose.put(attributeName, attributeValue);
}
}
arguments.addAll(attributesToExpose.values());
return arguments.toArray(new Object[arguments.size()]);
}Currently, for a field named "fooField" of the class "barClass", the message keys would be ["barClass.fooField", "fooField"]. I believe this was implemented in #11073.
This can then be used in a message.properties file (for a @Size annotation) like so:
Size={0} must be between {1} and {2}
where {0} is either the localized field name (using the 2 keys described above) or the field name itself as a fallback.
This works great, but does not allow for using "namespaced" common field names in messages.properties like so:
labels.firstName=First Name
labels.lastName=Last Name
labels.etc=...
I coded this custom validator factory bean which achieves this:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public Validator getValidator() {
CustomValidator factory = new CustomValidator();
return factory;
}
private static class CustomValidator extends OptionalValidatorFactoryBean {
private static final String FIELD_NAME_PREFIX = "labels";
@Override
protected Object[] getArgumentsForConstraint(String objectName, String field,
ConstraintDescriptor<?> descriptor) {
Object[] arguments = super.getArgumentsForConstraint(objectName, field, descriptor);
// Add a custom message code for the field name
if (arguments.length > 0 && arguments[0] instanceof DefaultMessageSourceResolvable) {
DefaultMessageSourceResolvable fieldArgument = (DefaultMessageSourceResolvable) arguments[0];
String[] codes = fieldArgument.getCodes();
String[] extendedCodes = new String[codes.length + 1];
extendedCodes[0] = FIELD_NAME_PREFIX + Errors.NESTED_PATH_SEPARATOR + field;
System.arraycopy(codes, 0, extendedCodes, 1, codes.length);
arguments[0] = new DefaultMessageSourceResolvable(extendedCodes, field);
}
return arguments;
}
}
}Now, for the feature request: I think it would be useful to externalize the choice of field name message codes to a protected method which could be overwritten by child classes. In this way, the implementation would be much cleaner and more robust.
To be clear, I'm proposing to change this line in SpringValidatorAdapter :
String[] codes = new String[] {objectName + Errors.NESTED_PATH_SEPARATOR + field, field};
to something like :
String[] codes = getFieldCodes(objectName, field);
Affects: 4.2.5
Referenced from: commits 696dcb7