Spring Boot中如何让HibernateValidator从HTTP请求头获取Locale
问题分析
当前配置存在几个关键问题导致HibernateValidator无法根据请求头Locale切换消息:
- LocaleResolver逻辑错误:自定义LocaleResolver的默认Locale使用了
Locale.getDefault()而非配置的默认值,且未将解析后的Locale存入LocaleContextHolder供后续验证使用。 - 异步方法Locale上下文丢失:
@Async方法执行时,线程池线程不会自动继承请求线程的LocaleContext,导致验证时无法获取请求头指定的Locale。 - MessageInterpolator未关联LocaleContext:未配置使用
LocaleContextHolder中Locale的消息插值器,HibernateValidator默认使用JVM的默认Locale。 - 消息配置冗余引号:
messages.properties中的消息包含多余引号,会导致最终返回的消息带无效引号。
解决方案
1. 修复LocaleResolver配置
修改CustomLocaleResolver,确保默认Locale使用配置值,并将解析后的Locale存入LocaleContextHolder:
public static class CustomLocaleResolver extends AcceptHeaderLocaleResolver { private List<Locale> LOCALES = Arrays.asList(new Locale("en"), new Locale("fr")); @Override public Locale resolveLocale(HttpServletRequest request) { String headerLang = request.getHeader("Accept-Language"); Locale resolvedLocale = headerLang == null || headerLang.isEmpty() ? getDefaultLocale() : Locale.lookup(Locale.LanguageRange.parse(headerLang), LOCALES); // 将解析后的Locale存入上下文,供验证逻辑使用 LocaleContextHolder.setLocale(resolvedLocale); return resolvedLocale; } }
同时调整localeResolver Bean的默认Locale配置:
@Bean public AcceptHeaderLocaleResolver localeResolver() { final CustomLocaleResolver resolver = new CustomLocaleResolver(); resolver.setSupportedLocales(Arrays.asList(Locale.FRANCE, Locale.US, Locale.UK)); resolver.setDefaultLocale(Locale.US); return resolver; }
2. 配置Locale上下文传递的异步线程池
修改asyncExecutor Bean,添加TaskDecorator将请求线程的Locale上下文传递到异步线程:
@Bean("threadPoolTaskExecutor") public TaskExecutor asyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(50); executor.setMaxPoolSize(1000); executor.setQueueCapacity(50); executor.setWaitForTasksToCompleteOnShutdown(true); executor.setThreadNamePrefix("Async-"); // 添加任务装饰器,传递Locale上下文 executor.setTaskDecorator(runnable -> { LocaleContext context = LocaleContextHolder.getLocaleContext(); return () -> { try { LocaleContextHolder.setLocaleContext(context); runnable.run(); } finally { LocaleContextHolder.resetLocaleContext(); } }; }); executor.initialize(); return executor; }
3. 修复消息文件与Validator配置
调整消息文件格式
去掉messages.properties中的冗余引号:
# messages.properties msisdn.error.empty=MSISDN is empty
配置Validator使用SpringMessageInterpolator
修改validator Bean,明确指定使用SpringMessageInterpolator,它会自动读取LocaleContextHolder中的Locale:
@Bean public LocalValidatorFactoryBean validator(MessageSource messageSource) { LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); factoryBean.setValidationMessageSource(messageSource); // 关联Spring的消息插值器,使用LocaleContextHolder的Locale factoryBean.setMessageInterpolator(new SpringMessageInterpolator(messageSource)); return factoryBean; }
4. 关联自定义Validator到Spring MVC
在ServiceConfig中重写getValidator方法,确保Spring MVC使用自定义的Validator:
@Override public Validator getValidator() { return validator(messageSource()); }
验证步骤
- 发送POST请求到
/api/v1/getSimData,请求体为{},请求头添加Accept-Language: fr,应返回法语错误消息Le MSISDN est vide。 - 发送请求时添加
Accept-Language: en,应返回英语错误消息MSISDN is empty。 - 不添加
Accept-Language头,应返回默认英语消息。
内容的提问来源于stack exchange,提问作者Roman Y
相关产品推荐
相关产品推荐

