Rework server.servlet.encoding properties to clarify when they apply

Closes gh-45394
This commit is contained in:
Andy Wilkinson
2025-06-04 20:20:12 +01:00
parent e225353e16
commit 4521866dd7
10 changed files with 174 additions and 155 deletions

View File

@@ -16,8 +16,6 @@
package org.springframework.boot.autoconfigure.http;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -27,19 +25,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration.HttpMessageConvertersAutoConfigurationRuntimeHints;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration.NotReactiveWebApplicationCondition;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration;
import org.springframework.boot.context.properties.bind.BindableRuntimeHintsRegistrar;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.web.servlet.server.Encoding;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.core.env.Environment;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
@@ -63,7 +56,6 @@ import org.springframework.http.converter.StringHttpMessageConverter;
@Conditional(NotReactiveWebApplicationCondition.class)
@Import({ JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class,
JsonbHttpMessageConvertersConfiguration.class })
@ImportRuntimeHints(HttpMessageConvertersAutoConfigurationRuntimeHints.class)
public class HttpMessageConvertersAutoConfiguration {
@Bean
@@ -74,13 +66,14 @@ public class HttpMessageConvertersAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(StringHttpMessageConverter.class)
@EnableConfigurationProperties(HttpMessageConvertersProperties.class)
protected static class StringHttpMessageConverterConfiguration {
@Bean
@ConditionalOnMissingBean
public StringHttpMessageConverter stringHttpMessageConverter(Environment environment) {
Encoding encoding = Binder.get(environment).bindOrCreate("server.servlet.encoding", Encoding.class);
StringHttpMessageConverter converter = new StringHttpMessageConverter(encoding.getCharset());
public StringHttpMessageConverter stringHttpMessageConverter(HttpMessageConvertersProperties properties) {
StringHttpMessageConverter converter = new StringHttpMessageConverter(
properties.getStringEncodingCharset());
converter.setWriteAcceptCharset(false);
return converter;
}
@@ -100,13 +93,4 @@ public class HttpMessageConvertersAutoConfiguration {
}
static class HttpMessageConvertersAutoConfigurationRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
BindableRuntimeHintsRegistrar.forTypes(Encoding.class).registerHints(hints, classLoader);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.http;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* {@link ConfigurationProperties @ConfigurationProperties} for HTTP message conversion.
*
* @author Andy Wilkinson
* @since 4.0.0
*/
@ConfigurationProperties("spring.http.converters")
public class HttpMessageConvertersProperties {
/**
* The charset to use for String conversion.
*/
private Charset stringEncodingCharset = StandardCharsets.UTF_8;
public Charset getStringEncodingCharset() {
return this.stringEncodingCharset;
}
public void setStringEncodingCharset(Charset stringEncodingCharset) {
this.stringEncodingCharset = stringEncodingCharset;
}
}

View File

@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import io.undertow.UndertowOptions;
@@ -40,7 +41,6 @@ import org.springframework.boot.web.server.Http2;
import org.springframework.boot.web.server.MimeMappings;
import org.springframework.boot.web.server.Shutdown;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.servlet.server.Encoding;
import org.springframework.boot.web.servlet.server.Jsp;
import org.springframework.boot.web.servlet.server.Session;
import org.springframework.util.StringUtils;
@@ -266,7 +266,6 @@ public class ServerProperties {
*/
private boolean registerDefaultServlet = false;
@NestedConfigurationProperty
private final Encoding encoding = new Encoding();
@NestedConfigurationProperty
@@ -1970,4 +1969,21 @@ public class ServerProperties {
}
public static class Encoding {
/**
* Mapping of locale to charset for response encoding.
*/
private Map<Locale, Charset> mapping;
public Map<Locale, Charset> getMapping() {
return this.mapping;
}
public void setMapping(Map<Locale, Charset> mapping) {
this.mapping = mapping;
}
}
}

View File

@@ -22,73 +22,34 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProp
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.boot.web.servlet.server.Encoding;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.web.filter.CharacterEncodingFilter;
/**
* {@link EnableAutoConfiguration Auto-configuration} for configuring the encoding to use
* in web applications.
* in Servlet web applications.
*
* @author Stephane Nicoll
* @author Brian Clozel
* @since 2.0.0
*/
@AutoConfiguration
@EnableConfigurationProperties(ServerProperties.class)
@EnableConfigurationProperties(ServletEncodingProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnBooleanProperty(name = "server.servlet.encoding.enabled", matchIfMissing = true)
@ConditionalOnBooleanProperty(name = "spring.servlet.encoding.enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final Encoding properties;
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
public CharacterEncodingFilter characterEncodingFilter(ServletEncodingProperties properties) {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
filter.setEncoding(properties.getCharset().name());
filter.setForceRequestEncoding(properties.shouldForce(ServletEncodingProperties.HttpMessageType.REQUEST));
filter.setForceResponseEncoding(properties.shouldForce(ServletEncodingProperties.HttpMessageType.RESPONSE));
return filter;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
static class LocaleCharsetMappingsCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final Encoding properties;
LocaleCharsetMappingsCustomizer(Encoding properties) {
this.properties = properties;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
if (this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}
}
@Override
public int getOrder() {
return 0;
}
}
}

View File

@@ -14,25 +14,21 @@
* limitations under the License.
*/
package org.springframework.boot.web.servlet.server;
package org.springframework.boot.autoconfigure.web.servlet;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationPropertiesSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for server HTTP encoding.
* {@link ConfigurationProperties @ConfigurationProperties} for Servlet encoding.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Brian Clozel
* @since 2.3.0
* @author Andy Wilkinson
* @since 4.0.0
*/
@ConfigurationPropertiesSource
public class Encoding {
@ConfigurationProperties("spring.servlet.encoding")
public class ServletEncodingProperties {
/**
* Default HTTP encoding for Servlet applications.
@@ -62,11 +58,6 @@ public class Encoding {
*/
private Boolean forceResponse;
/**
* Mapping of locale to charset for response encoding.
*/
private Map<Locale, Charset> mapping;
public Charset getCharset() {
return this.charset;
}
@@ -99,21 +90,13 @@ public class Encoding {
this.forceResponse = forceResponse;
}
public Map<Locale, Charset> getMapping() {
return this.mapping;
}
public void setMapping(Map<Locale, Charset> mapping) {
this.mapping = mapping;
}
public boolean shouldForce(Type type) {
Boolean force = (type != Type.REQUEST) ? this.forceResponse : this.forceRequest;
public boolean shouldForce(HttpMessageType type) {
Boolean force = (type != HttpMessageType.REQUEST) ? this.forceResponse : this.forceRequest;
if (force == null) {
force = this.force;
}
if (force == null) {
force = (type == Type.REQUEST);
force = (type == HttpMessageType.REQUEST);
}
return force;
}
@@ -121,7 +104,7 @@ public class Encoding {
/**
* Type of HTTP message to consider for encoding configuration.
*/
public enum Type {
public enum HttpMessageType {
/**
* HTTP request message.

View File

@@ -96,6 +96,7 @@ public class ServletWebServerFactoryCustomizer
.whenNot(CollectionUtils::isEmpty)
.to(factory::setCookieSameSiteSuppliers);
map.from(this.serverProperties::getMimeMappings).to(factory::addMimeMappings);
map.from(this.serverProperties.getServlet().getEncoding()::getMapping).to(factory::setLocaleCharsetMappings);
this.webListenerRegistrars.forEach((registrar) -> registrar.register(factory));
}

View File

@@ -126,11 +126,54 @@
"name": "server.reactive.session.cookie.secure",
"description": "Whether to always mark the cookie as secure."
},
{
"name": "server.servlet.encoding.charset",
"type": "java.nio.charset.Charset",
"description": "Charset of HTTP requests and responses. Added to the Content-Type header if not set explicitly.",
"deprecation": {
"replacement": "spring.servlet.encoding.charset",
"level": "error"
}
},
{
"name": "server.servlet.encoding.enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable http encoding support.",
"defaultValue": true
"defaultValue": true,
"deprecation": {
"replacement": "spring.servlet.encoding.enabled",
"level": "error"
}
},
{
"name": "server.servlet.encoding.force",
"type": "java.lang.Boolean",
"description": "Whether to force the encoding to the configured charset on HTTP requests and responses.",
"defaultValue": false,
"deprecation": {
"replacement": "spring.servlet.encoding.force",
"level": "error"
}
},
{
"name": "server.servlet.encoding.force-request",
"type": "java.lang.Boolean",
"description": "Whether to force the encoding to the configured charset on HTTP requests. Defaults to true when force has not been specified.",
"defaultValue": true,
"deprecation": {
"replacement": "spring.servlet.encoding.force-request",
"level": "error"
}
},
{
"name": "server.servlet.encoding.force-response",
"type": "java.lang.Boolean",
"description": "Whether to force the encoding to the configured charset on HTTP responses.",
"defaultValue": false,
"deprecation": {
"replacement": "spring.servlet.encoding.force-response",
"level": "error"
}
},
{
"name": "server.servlet.jsp.class-name",
@@ -2525,6 +2568,12 @@
"level": "error"
}
},
{
"name": "spring.servlet.encoding.enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable Servlet HTTP encoding support.",
"defaultValue": true
},
{
"name": "spring.session.redis.cleanup-cron",
"defaultValue": "0 * * * * *"

View File

@@ -23,12 +23,9 @@ import com.google.gson.Gson;
import jakarta.json.bind.Jsonb;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration.HttpMessageConvertersAutoConfigurationRuntimeHints;
import org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration;
@@ -39,7 +36,6 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.servlet.server.Encoding;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
@@ -313,7 +309,7 @@ class HttpMessageConvertersAutoConfigurationTests {
void whenEncodingCharsetIsConfiguredThenStringMessageConverterUsesSpecificCharset() {
new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(HttpMessageConvertersAutoConfiguration.class))
.withPropertyValues("server.servlet.encoding.charset=UTF-16")
.withPropertyValues("spring.http.converters.string-encoding-charset=UTF-16")
.run((context) -> {
assertThat(context).hasSingleBean(StringHttpMessageConverter.class);
assertThat(context.getBean(StringHttpMessageConverter.class).getDefaultCharset())
@@ -331,19 +327,6 @@ class HttpMessageConvertersAutoConfigurationTests {
});
}
@Test
void shouldRegisterHints() {
RuntimeHints hints = new RuntimeHints();
new HttpMessageConvertersAutoConfigurationRuntimeHints().registerHints(hints, getClass().getClassLoader());
assertThat(RuntimeHintsPredicates.reflection().onType(Encoding.class)).accepts(hints);
assertThat(RuntimeHintsPredicates.reflection().onMethodInvocation(Encoding.class, "getCharset")).accepts(hints);
assertThat(RuntimeHintsPredicates.reflection().onMethodInvocation(Encoding.class, "setCharset")).accepts(hints);
assertThat(RuntimeHintsPredicates.reflection().onMethodInvocation(Encoding.class, "isForce")).accepts(hints);
assertThat(RuntimeHintsPredicates.reflection().onMethodInvocation(Encoding.class, "setForce")).accepts(hints);
assertThat(RuntimeHintsPredicates.reflection().onMethodInvocation(Encoding.class, "shouldForce"))
.rejects(hints);
}
private ApplicationContextRunner allOptionsRunner() {
return this.contextRunner.withConfiguration(AutoConfigurations.of(GsonAutoConfiguration.class,
JacksonAutoConfiguration.class, JsonbAutoConfiguration.class));

View File

@@ -16,11 +16,8 @@
package org.springframework.boot.autoconfigure.web.servlet;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import jakarta.servlet.Filter;
import org.junit.jupiter.api.AfterEach;
@@ -28,7 +25,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext;
import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter;
@@ -69,53 +65,53 @@ class HttpEncodingAutoConfigurationTests {
@Test
void disableConfiguration() {
load(EmptyConfiguration.class, "server.servlet.encoding.enabled:false");
load(EmptyConfiguration.class, "spring.servlet.encoding.enabled:false");
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> this.context.getBean(CharacterEncodingFilter.class));
}
@Test
void customConfiguration() {
load(EmptyConfiguration.class, "server.servlet.encoding.charset:ISO-8859-15",
"server.servlet.encoding.force:false");
load(EmptyConfiguration.class, "spring.servlet.encoding.charset:ISO-8859-15",
"spring.servlet.encoding.force:false");
CharacterEncodingFilter filter = this.context.getBean(CharacterEncodingFilter.class);
assertCharacterEncodingFilter(filter, "ISO-8859-15", false, false);
}
@Test
void customFilterConfiguration() {
load(FilterConfiguration.class, "server.servlet.encoding.charset:ISO-8859-15",
"server.servlet.encoding.force:false");
load(FilterConfiguration.class, "spring.servlet.encoding.charset:ISO-8859-15",
"spring.servlet.encoding.force:false");
CharacterEncodingFilter filter = this.context.getBean(CharacterEncodingFilter.class);
assertCharacterEncodingFilter(filter, "US-ASCII", false, false);
}
@Test
void forceRequest() {
load(EmptyConfiguration.class, "server.servlet.encoding.force-request:false");
load(EmptyConfiguration.class, "spring.servlet.encoding.force-request:false");
CharacterEncodingFilter filter = this.context.getBean(CharacterEncodingFilter.class);
assertCharacterEncodingFilter(filter, "UTF-8", false, false);
}
@Test
void forceResponse() {
load(EmptyConfiguration.class, "server.servlet.encoding.force-response:true");
load(EmptyConfiguration.class, "spring.servlet.encoding.force-response:true");
CharacterEncodingFilter filter = this.context.getBean(CharacterEncodingFilter.class);
assertCharacterEncodingFilter(filter, "UTF-8", true, true);
}
@Test
void forceRequestOverridesForce() {
load(EmptyConfiguration.class, "server.servlet.encoding.force:true",
"server.servlet.encoding.force-request:false");
load(EmptyConfiguration.class, "spring.servlet.encoding.force:true",
"spring.servlet.encoding.force-request:false");
CharacterEncodingFilter filter = this.context.getBean(CharacterEncodingFilter.class);
assertCharacterEncodingFilter(filter, "UTF-8", false, true);
}
@Test
void forceResponseOverridesForce() {
load(EmptyConfiguration.class, "server.servlet.encoding.force:true",
"server.servlet.encoding.force-response:false");
load(EmptyConfiguration.class, "spring.servlet.encoding.force:true",
"spring.servlet.encoding.force-response:false");
CharacterEncodingFilter filter = this.context.getBean(CharacterEncodingFilter.class);
assertCharacterEncodingFilter(filter, "UTF-8", true, false);
}
@@ -129,30 +125,6 @@ class HttpEncodingAutoConfigurationTests {
assertThat(beans.get(1)).isInstanceOf(HiddenHttpMethodFilter.class);
}
@Test
void noLocaleCharsetMapping() {
load(EmptyConfiguration.class);
Map<String, WebServerFactoryCustomizer<?>> beans = getWebServerFactoryCustomizerBeans();
assertThat(beans).hasSize(1);
assertThat(this.context.getBean(MockServletWebServerFactory.class).getLocaleCharsetMappings()).isEmpty();
}
@Test
void customLocaleCharsetMappings() {
load(EmptyConfiguration.class, "server.servlet.encoding.mapping.en:UTF-8",
"server.servlet.encoding.mapping.fr_FR:UTF-8");
Map<String, WebServerFactoryCustomizer<?>> beans = getWebServerFactoryCustomizerBeans();
assertThat(beans).hasSize(1);
assertThat(this.context.getBean(MockServletWebServerFactory.class).getLocaleCharsetMappings()).hasSize(2)
.containsEntry(Locale.ENGLISH, StandardCharsets.UTF_8)
.containsEntry(Locale.FRANCE, StandardCharsets.UTF_8);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<String, WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return (Map) this.context.getBeansOfType(WebServerFactoryCustomizer.class);
}
private void assertCharacterEncodingFilter(CharacterEncodingFilter actual, String encoding,
boolean forceRequestEncoding, boolean forceResponseEncoding) {
assertThat(actual.getEncoding()).isEqualTo(encoding);

View File

@@ -17,7 +17,9 @@
package org.springframework.boot.autoconfigure.web.servlet;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
@@ -38,9 +40,11 @@ import org.springframework.boot.web.servlet.server.Jsp;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.assertArg;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
/**
* Tests for {@link ServletWebServerFactoryCustomizer}.
@@ -193,6 +197,26 @@ class ServletWebServerFactoryCustomizerTests {
then(factory).should().setShutdown(assertArg((shutdown) -> assertThat(shutdown).isEqualTo(Shutdown.IMMEDIATE)));
}
@Test
void noLocaleCharsetMapping() {
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
then(factory).should(never()).setLocaleCharsetMappings(anyMap());
}
@Test
void customLocaleCharsetMappings() {
Map<String, String> map = Map.of("server.servlet.encoding.mapping.en", "UTF-8",
"server.servlet.encoding.mapping.fr_FR", "UTF-8");
bindProperties(map);
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
then(factory).should()
.setLocaleCharsetMappings((assertArg((mappings) -> assertThat(mappings).hasSize(2)
.containsEntry(Locale.ENGLISH, StandardCharsets.UTF_8)
.containsEntry(Locale.FRANCE, StandardCharsets.UTF_8))));
}
private void bindProperties(Map<String, String> map) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(map);
new Binder(source).bind("server", Bindable.ofInstance(this.properties));