使用 UTF-8 的 Hibernate-validator 验证 Vaadin Bean

Vaadin Bean validation with Hibernate-validator with UTF-8

首先,我是 Vaadin 7 的新手。当我发现 BeanFieldGroup.class 时,我只是在尝试一些 vaadin 演示。如我所见,这 class 将一个字段绑定到一个 bean 属性。在 bean 中,属性使用验证约束注释 (JSR 303) 进行注释。在这种情况下,我的 pom.xml 包括休眠验证程序依赖项:

<dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.1.0.CR1</version>
</dependency>

我制作了 ValidationMessage.properties 文件并在其中放了一些消息,(带有匈牙利字符)并且它已保存为 UTF-8 格式,例如:

validation.NotBlank=Árvíztűrő tükörfúrgép

这是带注释的 bean 属性:

@NotNull(message = "{validation.NotBlank}")
private String name = null;

我可以看到,当我将字段值更改为 null 时,Vaadin 向我显示了我之前的约束错误消息,但字符编码错误

另一方面 - 没有 Vaadin,但 Spring,我可以使用下一个简单的公式,其中字符编码适合我(如你所见,我使用 Spring ReloadableResourceBundleMessageSource):

private MessageSource MessageSource(){
    ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
    reloadableResourceBundleMessageSource.setCacheSeconds(5000);
    reloadableResourceBundleMessageSource.setFallbackToSystemLocale(false);
    reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8");
    Properties properties = new Properties();
    properties.setProperty("fileEncodings", "UTF-8");
    reloadableResourceBundleMessageSource.setFileEncodings(properties);
    reloadableResourceBundleMessageSource.setBasename("classpath:/locales/messages");

    return reloadableResourceBundleMessageSource;
}

@Bean
public LocalValidatorFactoryBean validator(){

    LocalValidatorFactoryBean factory = new LocalValidatorFactoryBean();
    factory.setValidationMessageSource(MessageSource());

    return factory;
}

我的问题是:有人能告诉我正确的解决方案吗?如何配置休眠验证器或 Vaadin 以读取 属性 具有 UTF-8 编码的文件?

默认情况下,读取属性文件时使用 ISO-8859-1 编码。该编码中不可用的字符必须使用 Unicode 转义符(“\u1234”)进行转换,例如使用 native2ascii tool. See this answer 到相关问题以获得更多详细信息。

或者,您可以插入自定义 ResourceBundleLocator,它使用 UTF-8 编码读取属性。 Hibernate Validator 参考指南 describes how to do this. As a starting point, you could take PlatformResourceBundleLocator 并根据您的要求进行调整。

Vaadin 的 BeanFieldGroup 使用另一个 Vaadin class BeanValidator 进行 JSR-303 验证。如果您在 BeanValidator.getJavaxBeanValidator()getJavaxBeanValidatorFactory() 方法处查看它的源代码,您会发现它正在构建自己的 ValidatorValidatorFactory 实例。您需要对其进行 subclass 并使用使用从 Spring 上下文获得的 ValidatorFactory 的方法覆盖 getJavaxBeanValidatorFactory() 方法,该方法已正确设置为使用 Spring 自己的 UTF-8 编码 MessageSource 正如您已经在示例中配置的那样。它还将帮助您进行调试,因为 Spring 的 MessageSource 可以重新加载,这是一个巨大的节省。

编辑: 在关于如何使用 subclassing 覆盖的评论中提出问题后,我意识到 Vaadin 这部分的当前状态 API 从 7.5.9 版本开始,我不能优雅地做到这一点。由于我们不能优雅地做到这一点,我们可以通过反射以 spring 组件实现的这个 hack 来快速而肮脏的方式来做到这一点:

@Component
public class VaadinValidationHack implements InitializingBean {

    @Autowired
    private ValidatorFactory validatorFactory;

    protected void hack() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        Field field = BeanValidator.class.getDeclaredField("factory");
        if (!field.isAccessible()) {
            field.setAccessible(true);
        }
        field.set(null, validatorFactory);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        hack();
    }

}