Can't get Hibernate Validator working with Spring MessageSource

I'm trying to get Hibernate Validator setup to use messages from a Spring MessageSource. I have the following setup in my messages-context.xml:

<bean id="messageSource"
    class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>WEB-INF/messages/messages</value>
            <value>WEB-INF/messages/exceptions</value>
            <value>WEB-INF/messages/fields</value>
            <value>WEB-INF/messages/buttons</value>
            <value>WEB-INF/messages/validation_errors</value>
        </list>
    </property>
</bean>

<bean id="validator"
    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="validationMessageSource" ref="messageSource" />
</bean>

<bean id="localeChangeInterceptor"
    class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName" value="lang" />
</bean>

<bean id="localeResolver"
    class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
    <property name="defaultLocale" value="en_GB" />
</bean>

I've tried a variety of approaches to how to pass the message key to the hibernate validator (both with and without enclosing {} and also without specificying a custom key - just using the default one:

@NotEmpty(message="validation.required.field")
@Length(max=255, message="{validation.too.long}")
private String firstName;

@NotEmpty(message="{validation.required.field}")
@Length(max=255, message="{validation.too.long}")
private String lastName;

@NotNull
@Past(message="{validation.must.be.past}")
private Date dateOfBirth;

@NotEmpty(message="{validation.required.field}")
@Length(max=255, message="{validation.too.long}")
private String email;

My validation_errors_en_GB.properties looks like this:

validation.required.field=this is a required field
validation.too.long=this field can only take {0} characters
validation.must.be.past=this date has to be in the past 
javax.validation.constraints.NotNull.message=Custom message

However, when the empty fields are validated, the messages displayed are this:

First name      validation.required.field
Last name       {validation.required.field}
Date of birth   may not be null
Email           {validation.required.field}

For whatever reason, the key of the message is always used - the actual message is never looked up. Any idea where I'm going wrong?

Thanks,

Russell

Answers


This had me stumped for a while, but the problem is that you need to register with Spring the Validator used to validate @Controller methods (thanks to this answer for that insight!)

So if you are using XML config do something along these lines:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="messageInterpolator" ref="messageSource"/>
</bean>

<mvc:annotation-driven validator="validator"/> 

And if you are using javaconfig, do something like this:

@EnableWebMVC
@Configuration
public MyWebAppContext extends WebMvcConfigurerAdapter {

@Bean
public LocalValidatorFactoryBean validator() {
    LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
    validatorFactoryBean.setValidationMessageSource(messageSource);
    return validatorFactoryBean;
}

@Override
public Validator getValidator() {
    return validator();
}

(see Spring Web MVC framework documentation)


It's because Hibernate validator is looking at another place for the error message resolver.

For the easiest thing to make it works, I think you can create a file name "ValidationMessages.properties" and put it in your classpath folder. Then put the error messages into that file (got from validation_errors_en_GB.properties)

By the way, the brackets are required when specifying error messages in model classes (message="{validation.too.long}")


Hibernate Validator is looking in a different place for the locale. Try this:

LocaleContextHolder.setLocale(locale);


Hibernate Validation is not aware of Spring's MessageSource. You will need to implement a MessageInterpolator. It may look something like below:

public class SpringMessageInterpolator implements MessageInterpolator {

    private final MessageResource messageResource;
    private final MessageInterpolator delegate;

    public SpringMessageInterpolator(MessageResource messageResource, MessageInterpolator delegate) {
        this.messageResource = messageResource;
        this.delegate = delegate;
    }

    @Override
    public String interpolate(String messageTemplate, Context context) {
        Locale locale = Locale.getDefault();
        return interpolate(messageTemplate, context, locale);
    }

    @Override
    public String interpolate(String messageTemplate, Context context, Locale locale) {
        try {
            Object[] args = {};
            return databaseMessageResource.getMessage(messageTemplate, args, locale);
        } catch (NoSuchMessageException ex) {
            return delegate.interpolate(messageTemplate, context, locale);
        }
    }

}

This has worked for me.

<bean id="messageSource"
    class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename">
        <value>i18n/messages</value>
    </property>
</bean>

<bean id="validator"
    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="messageInterpolator">
        <bean
            class="org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator">
            <constructor-arg index="0">
                <bean
                    class="org.springframework.validation.beanvalidation.MessageSourceResourceBundleLocator">
                    <constructor-arg index="0" ref="messageSource" />
                </bean>
            </constructor-arg>
        </bean>
    </property>
</bean>

<bean
    class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
    <property name="validator" ref="validator" />
</bean>


<mvc:annotation-driven validator="validator" />

Need Your Help

How to deploy Eclipse RCP app with secure properties

security properties eclipse-plugin eclipse-rcp desktop-application

I'm currently developing an Eclipse RCP e4 desktop application which will be connecting to external web based APIs. These APIs are secure and require the application to pass secure credentials to

GoJS makeSVG not generating proper HTML SVG object

javascript html internet-explorer svg gojs

I have been using the Diagram.makeSVG() method to generate an SVG out of my diagram. Afterwards i added the SVG to a new blank tab using this code (diagram is my Diagram Object):