spring启动,基于localecontextholder的hibernate验证程序语言

ffvjumwh  于 2021-07-13  发布在  Java
关注(0)|答案(1)|浏览(275)

我有一个使用jpa、hibernate等的springboot2.4.2rest应用程序 MessageSource 对于应用程序错误(位于i18n/messages中),默认 ValidationMessages 用于bean验证。
这是我配置的一部分:

public static Set<Locale> LOCALES = Set.of(new Locale("en"), new Locale("it"));

@Bean
    public LocaleResolver localeResolver() {
        SmartLocaleResolver localeResolver = new SmartLocaleResolver();
        return localeResolver;
    }

    public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            if (StringUtils.isBlank(request.getHeader("Accept-Language"))) {
                return Locale.getDefault();
            }
            List<Locale.LanguageRange> list = Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
            Locale locale = Locale.lookup(list, LOCALES);
            return locale;
        }
    }

    @Primary
    @Bean("messageSource")
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setAlwaysUseMessageFormat(true);
        messageSource.setBasenames("classpath:/i18n/messages");
        // set to true only for debugging
        messageSource.setUseCodeAsDefaultMessage(false);
        messageSource.setFallbackToSystemLocale(false);
        return messageSource;
    }

到目前为止,我的应用程序支持两种语言:it和en。现在的问题是,应用程序的消息在代理的语言(浏览器)中被正确地本地化,但验证错误却没有。
我发现hibernate使用默认语言环境(locale.getdefault()),要自定义行为,我应该自定义语言环境解析。
所以我尝试创建一个自定义的hibernatevalidator(我在entityfactory中设置):

@Bean
public MessageSource validationMessageSource() {
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setAlwaysUseMessageFormat(true);
    messageSource.setBasenames("classpath:/ValidationMessages");
    // set to true only for debugging
    messageSource.setUseCodeAsDefaultMessage(false);
    messageSource.setFallbackToSystemLocale(false);
    return messageSource;
}

 @Bean("hibernateValidator")
    public LocalValidatorFactoryBean hibernateValidator() {
        LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
        factoryBean.setValidationMessageSource(validationMessageSource());           
        return factoryBean;
    }

以及分解器:

public class HibernateLocaleResolver implements LocaleResolver {

    @Override
    public Locale resolve(LocaleResolverContext context) {
        return LocaleContextHolder.getLocale();
    }
}

这样做,区域设置解析可以正常工作,但参数替换不能。我的意思是这样的信息:

server.validators.ArraySize.message = The number of values must be between [{min}] and [{max}].

我有个例外:

"exception": "java.lang.IllegalArgumentException", "message": "can't parse argument number: min"

因此,我更改了上面的配置,添加了messageinterpolator:

factoryBean.setMessageInterpolator(new ResourceBundleMessageInterpolator(LOCALES, Locale.ENGLISH, new HibernateLocaleResolver(), false));

在这一点上,参数被正确解析,但是区域设置解析同样不起作用。
你能给我指出正确的方向,解释一下spring boot-hibernate组合验证器的最佳实践吗?

j2qf4p5b

j2qf4p5b1#

我解决了这个问题。我希望这能帮助别人。这是我的配置文件:

@Primary
@Bean("messageSource")
public MessageSource messageSource() {
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setAlwaysUseMessageFormat(true);
    messageSource.setBasenames("classpath:/i18n/messages");
    // set to true only for debugging
    messageSource.setUseCodeAsDefaultMessage(false);
    messageSource.setFallbackToSystemLocale(false);
    return messageSource;
}

@Bean
public MessageSource validationMessageSource() {
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setAlwaysUseMessageFormat(true);
    messageSource.setBasenames("classpath:/ValidationMessages");
    // set to true only for debugging
    messageSource.setUseCodeAsDefaultMessage(false);
    messageSource.setFallbackToSystemLocale(false);
    return messageSource;
}

@Bean
public LocalValidatorFactoryBean validator() {
    LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
    factoryBean.setValidationMessageSource(validationMessageSource());
    MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
    factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
    return factoryBean;
}

以及本地配置:

@Configuration
public class LocaleConfiguration implements WebMvcConfigurer {

    @Bean
    public LocaleResolver localeResolver() {
        SmartLocaleResolver localeResolver = new SmartLocaleResolver();
        return localeResolver;
    }

    public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            if (StringUtils.isBlank(request.getHeader("Accept-Language"))) {
                return Locale.getDefault();
            }
            List<Locale.LanguageRange> list = Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
            Locale locale = Locale.lookup(list, Constants.LOCALES);
            return locale;
        }
    }
}

我看到的最重要的区别是这些线条:

MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());

通过这种方式,插值器可以很好地工作,同时还可以实现消息的本地化。

相关问题