Add Locale to Charset Mapping for Servlet containers

This commit adds a new configuration key:

    spring.http.encoding.mapping.<locale>=<charset>

This allows to specify which default charset should be used for any
given Locale, if none has been provided already in the response itself.
This applies to all supported embedded servlet containers.

Fixes gh-6453
This commit is contained in:
Brian Clozel
2016-07-26 12:00:14 +02:00
parent 01f73d257f
commit a282710fc9
12 changed files with 205 additions and 5 deletions

View File

@@ -20,11 +20,15 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.HttpEncodingProperties.Type;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.filter.OrderedCharacterEncodingFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.filter.CharacterEncodingFilter;
/**
@@ -32,10 +36,12 @@ import org.springframework.web.filter.CharacterEncodingFilter;
* in web applications.
*
* @author Stephane Nicoll
* @author Brian Clozel
* @since 1.2.0
*/
@Configuration
@EnableConfigurationProperties(HttpEncodingProperties.class)
@ConditionalOnWebApplication
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
@@ -56,4 +62,30 @@ public class HttpEncodingAutoConfiguration {
return filter;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
private static class LocaleCharsetMappingsCustomizer implements EmbeddedServletContainerCustomizer, Ordered {
private final HttpEncodingProperties properties;
LocaleCharsetMappingsCustomizer(HttpEncodingProperties properties) {
this.properties = properties;
}
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (this.properties.getMapping() != null) {
container.setLocaleCharsetMappings(this.properties.getMapping());
}
}
@Override
public int getOrder() {
return 0;
}
}
}

View File

@@ -17,6 +17,8 @@
package org.springframework.boot.autoconfigure.web;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -24,6 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* Configuration properties for http encoding.
*
* @author Stephane Nicoll
* @author Brian Clozel
* @since 1.2.0
*/
@ConfigurationProperties(prefix = "spring.http.encoding")
@@ -53,6 +56,11 @@ public class HttpEncodingProperties {
*/
private Boolean forceResponse;
/**
* Locale to Encoding mapping.
*/
private Map<Locale, Charset> mapping;
public Charset getCharset() {
return this.charset;
}
@@ -85,6 +93,14 @@ public class HttpEncodingProperties {
this.forceResponse = forceResponse;
}
public Map<Locale, Charset> getMapping() {
return this.mapping;
}
public void setMapping(Map<Locale, Charset> mapping) {
this.mapping = mapping;
}
boolean shouldForce(Type type) {
Boolean force = (type == Type.REQUEST ? this.forceRequest : this.forceResponse);
if (force == null) {

View File

@@ -16,8 +16,11 @@
package org.springframework.boot.autoconfigure.web;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.Filter;
@@ -27,13 +30,17 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter;
import org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
@@ -49,7 +56,7 @@ public class HttpEncodingAutoConfigurationTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private AnnotationConfigApplicationContext context;
private AnnotationConfigWebApplicationContext context;
@After
public void close() {
@@ -135,6 +142,31 @@ public class HttpEncodingAutoConfigurationTests {
assertThat(beans.get(1)).isInstanceOf(HiddenHttpMethodFilter.class);
}
@Test
public void noLocaleCharsetMapping() {
load(EmptyConfiguration.class);
Map<String, EmbeddedServletContainerCustomizer> beans =
this.context.getBeansOfType(EmbeddedServletContainerCustomizer.class);
assertThat(beans.size()).isEqualTo(1);
assertThat(this.context.getBean(MockEmbeddedServletContainerFactory.class)
.getLocaleCharsetMappings().size()).isEqualTo(0);
}
@Test
public void customLocaleCharsetMappings() {
load(EmptyConfiguration.class, "spring.http.encoding.mapping.en:UTF-8",
"spring.http.encoding.mapping.fr_FR:UTF-8");
Map<String, EmbeddedServletContainerCustomizer> beans =
this.context.getBeansOfType(EmbeddedServletContainerCustomizer.class);
assertThat(beans.size()).isEqualTo(1);
assertThat(this.context.getBean(MockEmbeddedServletContainerFactory.class)
.getLocaleCharsetMappings().size()).isEqualTo(2);
assertThat(this.context.getBean(MockEmbeddedServletContainerFactory.class)
.getLocaleCharsetMappings().get(Locale.ENGLISH)).isEqualTo(Charset.forName("UTF-8"));
assertThat(this.context.getBean(MockEmbeddedServletContainerFactory.class)
.getLocaleCharsetMappings().get(Locale.FRANCE)).isEqualTo(Charset.forName("UTF-8"));
}
private void assertCharacterEncodingFilter(CharacterEncodingFilter actual,
String encoding, boolean forceRequestEncoding,
boolean forceResponseEncoding) {
@@ -147,12 +179,14 @@ public class HttpEncodingAutoConfigurationTests {
this.context = doLoad(new Class<?>[] { config }, environment);
}
private AnnotationConfigApplicationContext doLoad(Class<?>[] configs,
private AnnotationConfigWebApplicationContext doLoad(Class<?>[] configs,
String... environment) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
EnvironmentTestUtils.addEnvironment(applicationContext, environment);
applicationContext.register(configs);
applicationContext.register(HttpEncodingAutoConfiguration.class);
applicationContext.register(MinimalWebAutoConfiguration.class,
HttpEncodingAutoConfiguration.class);
applicationContext.setServletContext(new MockServletContext());
applicationContext.refresh();
return applicationContext;
}
@@ -190,4 +224,19 @@ public class HttpEncodingAutoConfigurationTests {
}
@Configuration
static class MinimalWebAutoConfiguration {
@Bean
public MockEmbeddedServletContainerFactory mockEmbeddedServletContainerFactory() {
return new MockEmbeddedServletContainerFactory();
}
@Bean
public EmbeddedServletContainerCustomizerBeanPostProcessor
embeddedServletContainerCustomizerBeanPostProcessor() {
return new EmbeddedServletContainerCustomizerBeanPostProcessor();
}
}
}