I just start a new project based on Spring boot 2 + Webflux. On upgrading version of spring boot and replace spring-boot-starter-web
with spring-boot-starter-webflux
classes like
are missing. How now can I configure defaultLocale, and interceptor to change the language?
Just add a WebFilter
that sets the Accept-Language
header from the value of a query parameter. The following example gets the language from the language query parameter on URIs like http://localhost:8080/examples?language=es
:
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
import reactor.core.publisher.Mono;
import static org.springframework.util.StringUtils.isEmpty;
@Component
public class LanguageQueryParameterWebFilter implements WebFilter {
private final ApplicationContext applicationContext;
private HttpWebHandlerAdapter httpWebHandlerAdapter;
public LanguageQueryParameterWebFilter(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@EventListener(ApplicationReadyEvent.class)
public void loadHttpHandler() {
this.httpWebHandlerAdapter = applicationContext.getBean(HttpWebHandlerAdapter.class);
}
@Override
public Mono<Void> filter(final ServerWebExchange exchange, final WebFilterChain chain) {
final ServerHttpRequest request = exchange.getRequest();
final MultiValueMap<String, String> queryParams = request.getQueryParams();
final String languageValue = queryParams.getFirst("language");
final ServerWebExchange localizedExchange = getServerWebExchange(languageValue, exchange);
return chain.filter(localizedExchange);
}
private ServerWebExchange getServerWebExchange(final String languageValue, final ServerWebExchange exchange) {
return isEmpty(languageValue)
? exchange
: getLocalizedServerWebExchange(languageValue, exchange);
}
private ServerWebExchange getLocalizedServerWebExchange(final String languageValue, final ServerWebExchange exchange) {
final ServerHttpRequest httpRequest = exchange.getRequest()
.mutate()
.headers(httpHeaders -> httpHeaders.set("Accept-Language", languageValue))
.build();
return new DefaultServerWebExchange(httpRequest, exchange.getResponse(),
httpWebHandlerAdapter.getSessionManager(), httpWebHandlerAdapter.getCodecConfigurer(),
httpWebHandlerAdapter.getLocaleContextResolver());
}
}
It uses @EventListener(ApplicationReadyEvent.class)
in order to avoid cyclic dependencies.
Feel free to test it and provide feedback on this POC.
With spring-boot-starter-webflux, there are
For example, to use a query parameter "lang" to explicitly control the locale:
Implement LocaleContextResolver
, so that
resolveLocaleContext()
returns a SimpleLocaleContext
determined by a GET parameter of "lang". I name this implementation QueryParamLocaleContextResolver
. Note that the default LocaleContextResolver
is an org.springframework.web.server.i18n.AcceptHeaderLocaleContextResolver
.
Create a @Configuration
class that extends DelegatingWebFluxConfiguration
. Override DelegatingWebFluxConfiguration.localeContextResolver()
to return QueryParamLocaleContextResolver
that we just created in step 1. Name this configuration class WebConfig
.
In WebConfig
, override DelegatingWebFluxConfiguration.configureViewResolvers()
and add the ThymeleafReactiveViewResolver
bean as a view resolver. We do this because, for some reason, DelegatingWebFluxConfiguration
will miss ThymeleafReactiveViewResolver
after step 2.
Also, I have to mention that, to use i18n with the reactive stack, this bean is necessary:
@Bean
public MessageSource messageSource() {
final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:/messages");
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(5);
return messageSource;
}
After creating a natural template, some properties files, and a controller, you will see that:
localhost:8080/test?lang=zh
gives you the Chinese version
localhost:8080/test?lang=en
gives you the English version
Just don't forget <meta charset="UTF-8">
in <head>
, otherwise you may see some nasty display of Chinese characters.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With