How to add custom LocaleResolverConfiguration in webflux

Viewed 1087

I added the following @Bean in the classpath for overriding default LocaleContextResolver in spring webflux

@Configuration
public class LocaleResolverConfiguration {

  @Bean(WebHttpHandlerBuilder.LOCALE_CONTEXT_RESOLVER_BEAN_NAME)
  public LocaleContextResolver localeContextResolver() {
    return new LocaleContextResolver() {
      @Override
      public LocaleContext resolveLocaleContext(ServerWebExchange exchange) {
        final String langParam = exchange.getRequest().getQueryParams().getFirst("lang");
        if (langParam == null) {
          return new SimpleLocaleContext(Locale.getDefault());
        } else {
          return new SimpleLocaleContext(Locale.forLanguageTag(langParam));
        }
      }

      @Override
      public void setLocaleContext(ServerWebExchange exchange, LocaleContext localeContext) {
        throw new UnsupportedOperationException(
            "Cannot change HTTP accept header - use a different locale context resolution strategy");
      }
    };
  }

}

But the application startup fails with the following error

The bean 'localeContextResolver', defined in class path resource [org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration$EnableWebFluxConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/acme/webflux/config/LocaleResolverWebFluxConfiguration.class] and overriding is disabled.

Why is webflux not backing off when we already provided our own LocaleContextResolver localeContextResolver()?

Is there some other way to achieve the same thing?

2 Answers

I did the following hack as I could not find any reasonable way to modify the localeContextResolver without getting rid of autoconfiguration. But this is just a hack, not a reasonable solution

@Configuration
public class LocaleResolverConfiguration {

  @Bean
  public CommandLineRunner localeContextResolverCustomizer(HttpHandler httpHandler) {
    assert httpHandler instanceof HttpWebHandlerAdapter;
    return args -> {
      ((HttpWebHandlerAdapter) httpHandler).setLocaleContextResolver(new LocaleContextResolver() {
        @Override
        public LocaleContext resolveLocaleContext(ServerWebExchange exchange) {
          final String langParam = exchange.getRequest().getQueryParams().getFirst("lang");
          if (langParam == null) {
            return new SimpleLocaleContext(Locale.getDefault());
          } else {
            return new SimpleLocaleContext(Locale.forLanguageTag(langParam));
          }
        }

        @Override
        public void setLocaleContext(ServerWebExchange exchange, LocaleContext localeContext) {
          throw new UnsupportedOperationException("Cannot change HTTP parameter - use a different locale context resolution strategy");
        }
      });
    };
  }

}

In webflux I have used the following snippet of code and it works. If lang parameter is found in the URL so the associated messages are resolved, else it resolves messages associated to HTTP request header Content-Language. If both are not set, so default messages with be resolved:

@Configuration
public class LocaleResolverConfig {

   @Bean
   @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
   public HttpHandler httpHandler(ApplicationContext applicationContext) {
       HttpHandler delegate = WebHttpHandlerBuilder.applicationContext(applicationContext).build();
       return new HttpWebHandlerAdapter(((HttpWebHandlerAdapter) delegate)) {
       @Override
       protected ServerWebExchange createExchange(
           ServerHttpRequest request, ServerHttpResponse response) {
                ServerWebExchange serverWebExchange = super.createExchange(request, response);

                final MultiValueMap<String, String> queryParams = request.getQueryParams();
                final String languageValue = queryParams.getFirst("lang");
                if (StringUtils.isNoneEmpty(languageValue)) {
                     LocaleContextHolder.setLocaleContext(() -> 
                          Locale.forLanguageTag(languageValue));
                } else {
                     LocaleContext localeContext = serverWebExchange.getLocaleContext();
                     if (localeContext != null) {
                          LocaleContextHolder.setLocaleContext(localeContext);
                     }
                }
                return serverWebExchange;
           }
       };
    }
 }
Related