【问题标题】:Can't get Hibernate Validator working with Spring MessageSource无法让 Hibernate Validator 与 Spring MessageSource 一起使用
【发布时间】:2011-10-16 14:54:22
【问题描述】:

我正在尝试设置 Hibernate Validator 以使用来自 Spring MessageSource 的消息。我的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>

我尝试了多种方法来将消息密钥传递给休眠验证器(包括和不包括 {},也没有指定自定义密钥 - 仅使用默认密钥:

@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;

我的validation_errors_en_GB.properties 看起来像这样:

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

但是,当验证空字段时,显示的消息是这样的:

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

无论出于何种原因,始终使用消息的键 - 永远不会查找实际消息。知道我哪里出错了吗?

谢谢,

罗素

【问题讨论】:

  • 我正在使用:Spring 3.05、Hibernate-Validator 4.2.0.Final、Hibernate-Core 3.3.2.GA 和 Hibernate-Annotations 3.4.0.GA
  • 我尝试使用较新版本的 hibernate-core 和注释,但在尝试创建 sessionFactory 时得到了 java.lang.IncompatibleClassChangeError: Implementing class,这表明版本之间存在某种不兼容 - 但是什么?有什么想法吗?
  • 大家提到如何配置spring来了解hibernate验证器和消息源,这里需要让hibernate知道选择springs消息源而不是自己的ValidationProperties文件

标签: hibernate spring-mvc localization validation


【解决方案1】:

这让我困惑了一段时间,但问题是您需要向 Spring 注册用于验证 @Controller 方法的 Validator(感谢 this answer 提供的洞察力!)

因此,如果您使用 XML 配置,请按照以下方式进行操作:

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

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

如果您使用的是 javaconfig,请执行以下操作:

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

(见Spring Web MVC framework documentation

【讨论】:

  • 感谢您的回答 - 我赞成它而不是接受它,因为自从发布这个问题以来,我已经离开了 Java,所以我不再有必要的设置来测试它。虽然它“看起来”像一个正确的答案,所以当我有时间测试它时,我会接受它。
  • 实际上我可能会收回 - 你的建议和我的 messages-context.xml 中已有的有什么区别?
  • 因为您需要明确告诉 Spring 使用哪个 Validator bean 进行 MVC 验证。仅仅在应用程序上下文中浮动一个验证器是不够的。
【解决方案2】:

这是因为 Hibernate 验证器正在寻找错误消息解析器的另一个位置。

为了使其工作最简单,我认为您可以创建一个文件名“ValidationMessages.properties”并将其放在您的类路径文件夹中。然后将错误消息放入该文件(来自validation_errors_en_GB.properties

顺便说一句,在模型类中指定错误消息时需要括号(message="{validation.too.long}"

【讨论】:

    【解决方案3】:

    Hibernate Validation 不知道 Spring 的 MessageSource。您将需要实现 MessageInterpolator。它可能如下所示:

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

    【讨论】:

      【解决方案4】:

      Hibernate Validator 正在寻找不同的语言环境。试试这个:

      LocaleContextHolder.setLocale(locale);

      【讨论】:

      • 设置在哪里?在控制器方法中?我试了一下,但没有效果,很遗憾。不过感谢您的建议。
      【解决方案5】:

      这对我有用。

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

      【讨论】:

        【解决方案6】:

        默认情况下,Hibernate 的验证器(或者更好的说法是 MessageInterpolator)检查 ValidationMessages.propertiesSpring 检查 Messages.properties 以解决验证问题消息。

        在 Spring 的领域中,您可以如下配置这些文件(假设消息存储在 CustomValidationMessages.properties 文件中,并且该文件位于 i18n 目录中):

        @Configuration
        public class ApplicationConfiguration {
            /*
             * Declaring the i18n messages location
             */
            @Bean
            public MessageSource messageSource() {
                ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
                messageSource.setBasenames("i18n/CustomValidationMessages");
                messageSource.setDefaultEncoding("UTF-8");
                return messageSource;
            }
        
            /*
             * For Spring Validator: import org.springframework.validation.Validator;
             *
             * For Hibernate(JSR-303) Validator: import javax.validation.Validator;
             */
            @Bean
            public Validator validator() {
                LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
                localValidatorFactoryBean.setValidationMessageSource(messageSource());
                return localValidatorFactoryBean;
            }
        }
        

        注意:注意评论部分提到的导入。

        类别实体(例如):

        @Entity
        public class Category {
            ...
            @NotEmpty(message = "{required.field.error}")
            private String name;
            ...
        }
        

        CustomValidationMessages.properties 包含验证消息:

        required.field.error = This filed cannot be empty
        

        Hibernate 可以提供两种不同格式的消息,即The interpolatedThe non-interpolated 错误消息。 getMessage() 为您提供插值(此字段不能为空),getMessageTemplate() 为您提供非插值({required.field.error})

        import javax.validation.Validator;
        import javax.validation.ConstraintViolation;
        ..
        @Service
        public class CategoryService {
        
            @Autowired
            private Validator validator;
        
            private void insertNewCategory(Category category) {
                Set<ConstraintViolation<Category>> violations  = validator.validate(category);
                if (!violations.isEmpty()) {
                    for (ConstraintViolation<Category> constraintViolation : violations) {
                        System.out.println("interpolated: " +  constraintViolation.getMessage());
                        System.out.println("non interpolated: " + constraintViolation.getMessageTemplate());
                    }
                }
            }
            ....
        }
        

        输出:

        interpolated: This filed cannot be empty
        non interpolated: {required.field.error}
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-05
          • 1970-01-01
          • 2015-07-06
          • 1970-01-01
          • 2018-08-19
          相关资源
          最近更新 更多