Can't get Hibernate Validator working with Spr

2020-06-17 06:59发布

问题:

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

回答1:

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)



回答2:

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}")



回答3:

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

LocaleContextHolder.setLocale(locale);



回答4:

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);
        }
    }

}


回答5:

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" />