Move servlet http encoding auto-configuration into spring-boot-servlet
This commit is contained in:
committed by
Phillip Webb
parent
d0e7766d72
commit
8efbebb9b3
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.servlet.autoconfigure;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
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.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.filter.CharacterEncodingFilter;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for configuring the encoding to use
|
||||
* in Servlet web applications.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(ServletEncodingProperties.class)
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@ConditionalOnClass(CharacterEncodingFilter.class)
|
||||
@ConditionalOnBooleanProperty(name = "spring.servlet.encoding.enabled", matchIfMissing = true)
|
||||
public class HttpEncodingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CharacterEncodingFilter characterEncodingFilter(ServletEncodingProperties properties) {
|
||||
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
|
||||
filter.setEncoding(properties.getCharset().name());
|
||||
filter.setForceRequestEncoding(properties.shouldForce(ServletEncodingProperties.HttpMessageType.REQUEST));
|
||||
filter.setForceResponseEncoding(properties.shouldForce(ServletEncodingProperties.HttpMessageType.RESPONSE));
|
||||
return filter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.servlet.autoconfigure;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* {@link ConfigurationProperties @ConfigurationProperties} for Servlet encoding.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.servlet.encoding")
|
||||
public class ServletEncodingProperties {
|
||||
|
||||
/**
|
||||
* Default HTTP encoding for Servlet applications.
|
||||
*/
|
||||
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* Charset of HTTP requests and responses. Added to the "Content-Type" header if not
|
||||
* set explicitly.
|
||||
*/
|
||||
private Charset charset = DEFAULT_CHARSET;
|
||||
|
||||
/**
|
||||
* Whether to force the encoding to the configured charset on HTTP requests and
|
||||
* responses.
|
||||
*/
|
||||
private Boolean force;
|
||||
|
||||
/**
|
||||
* Whether to force the encoding to the configured charset on HTTP requests. Defaults
|
||||
* to true when "force" has not been specified.
|
||||
*/
|
||||
private Boolean forceRequest;
|
||||
|
||||
/**
|
||||
* Whether to force the encoding to the configured charset on HTTP responses.
|
||||
*/
|
||||
private Boolean forceResponse;
|
||||
|
||||
public Charset getCharset() {
|
||||
return this.charset;
|
||||
}
|
||||
|
||||
public void setCharset(Charset charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
public boolean isForce() {
|
||||
return Boolean.TRUE.equals(this.force);
|
||||
}
|
||||
|
||||
public void setForce(boolean force) {
|
||||
this.force = force;
|
||||
}
|
||||
|
||||
public boolean isForceRequest() {
|
||||
return Boolean.TRUE.equals(this.forceRequest);
|
||||
}
|
||||
|
||||
public void setForceRequest(boolean forceRequest) {
|
||||
this.forceRequest = forceRequest;
|
||||
}
|
||||
|
||||
public boolean isForceResponse() {
|
||||
return Boolean.TRUE.equals(this.forceResponse);
|
||||
}
|
||||
|
||||
public void setForceResponse(boolean forceResponse) {
|
||||
this.forceResponse = forceResponse;
|
||||
}
|
||||
|
||||
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 == HttpMessageType.REQUEST);
|
||||
}
|
||||
return force;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of HTTP message to consider for encoding configuration.
|
||||
*/
|
||||
public enum HttpMessageType {
|
||||
|
||||
/**
|
||||
* HTTP request message.
|
||||
*/
|
||||
REQUEST,
|
||||
/**
|
||||
* HTTP response message.
|
||||
*/
|
||||
RESPONSE
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"groups": [],
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.http.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": "server.servlet.encoding.charset",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.http.encoding.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether to enable http encoding support.",
|
||||
"defaultValue": true,
|
||||
"deprecation": {
|
||||
"replacement": "server.servlet.encoding.enabled",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.http.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": "server.servlet.encoding.force",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.http.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": "server.servlet.encoding.force-request",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.http.encoding.force-response",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether to force the encoding to the configured charset on HTTP responses.",
|
||||
"defaultValue": false,
|
||||
"deprecation": {
|
||||
"replacement": "server.servlet.encoding.force-response",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.http.encoding.mapping",
|
||||
"type": "java.util.Map<java.util.Locale,java.nio.charset.Charset>",
|
||||
"description": "Locale in which to encode mapping.",
|
||||
"deprecation": {
|
||||
"replacement": "server.servlet.encoding.mapping",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.http.log-request-details",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether logging of (potentially sensitive) request details at DEBUG and TRACE level is allowed.",
|
||||
"defaultValue": false,
|
||||
"deprecation": {
|
||||
"replacement": "spring.mvc.log-request-details",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.servlet.encoding.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether to enable Servlet HTTP encoding support.",
|
||||
"defaultValue": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
org.springframework.boot.servlet.actuate.autoconfigure.ServletManagementContextAutoConfiguration
|
||||
org.springframework.boot.servlet.actuate.autoconfigure.exchanges.ServletHttpExchangesAutoConfiguration
|
||||
org.springframework.boot.servlet.actuate.autoconfigure.mappings.ServletMappingsAutoConfiguration
|
||||
org.springframework.boot.servlet.autoconfigure.MultipartAutoConfiguration
|
||||
org.springframework.boot.servlet.autoconfigure.HttpEncodingAutoConfiguration
|
||||
org.springframework.boot.servlet.autoconfigure.MultipartAutoConfiguration
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.servlet.autoconfigure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.web.context.servlet.AnnotationConfigServletWebApplicationContext;
|
||||
import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter;
|
||||
import org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter;
|
||||
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.filter.CharacterEncodingFilter;
|
||||
import org.springframework.web.filter.HiddenHttpMethodFilter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link HttpEncodingAutoConfiguration}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class HttpEncodingAutoConfigurationTests {
|
||||
|
||||
private AnnotationConfigServletWebApplicationContext context;
|
||||
|
||||
@AfterEach
|
||||
void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultConfiguration() {
|
||||
load(EmptyConfiguration.class);
|
||||
CharacterEncodingFilter filter = this.context.getBean(CharacterEncodingFilter.class);
|
||||
assertCharacterEncodingFilter(filter, "UTF-8", true, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void disableConfiguration() {
|
||||
load(EmptyConfiguration.class, "spring.servlet.encoding.enabled:false");
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> this.context.getBean(CharacterEncodingFilter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customConfiguration() {
|
||||
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, "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, "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, "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, "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, "spring.servlet.encoding.force:true",
|
||||
"spring.servlet.encoding.force-response:false");
|
||||
CharacterEncodingFilter filter = this.context.getBean(CharacterEncodingFilter.class);
|
||||
assertCharacterEncodingFilter(filter, "UTF-8", true, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void filterIsOrderedHighest() {
|
||||
load(OrderedConfiguration.class);
|
||||
List<Filter> beans = new ArrayList<>(this.context.getBeansOfType(Filter.class).values());
|
||||
AnnotationAwareOrderComparator.sort(beans);
|
||||
assertThat(beans.get(0)).isInstanceOf(CharacterEncodingFilter.class);
|
||||
assertThat(beans.get(1)).isInstanceOf(HiddenHttpMethodFilter.class);
|
||||
}
|
||||
|
||||
private void assertCharacterEncodingFilter(CharacterEncodingFilter actual, String encoding,
|
||||
boolean forceRequestEncoding, boolean forceResponseEncoding) {
|
||||
assertThat(actual.getEncoding()).isEqualTo(encoding);
|
||||
assertThat(actual.isForceRequestEncoding()).isEqualTo(forceRequestEncoding);
|
||||
assertThat(actual.isForceResponseEncoding()).isEqualTo(forceResponseEncoding);
|
||||
}
|
||||
|
||||
private void load(Class<?> config, String... environment) {
|
||||
this.context = doLoad(new Class<?>[] { config }, environment);
|
||||
}
|
||||
|
||||
private AnnotationConfigServletWebApplicationContext doLoad(Class<?>[] configs, String... environment) {
|
||||
AnnotationConfigServletWebApplicationContext applicationContext = new AnnotationConfigServletWebApplicationContext();
|
||||
TestPropertyValues.of(environment).applyTo(applicationContext);
|
||||
applicationContext.register(configs);
|
||||
applicationContext.register(HttpEncodingAutoConfiguration.class);
|
||||
applicationContext.setServletContext(new MockServletContext());
|
||||
applicationContext.refresh();
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class EmptyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class FilterConfiguration {
|
||||
|
||||
@Bean
|
||||
CharacterEncodingFilter myCharacterEncodingFilter() {
|
||||
CharacterEncodingFilter filter = new CharacterEncodingFilter();
|
||||
filter.setEncoding("US-ASCII");
|
||||
filter.setForceEncoding(false);
|
||||
return filter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class OrderedConfiguration {
|
||||
|
||||
@Bean
|
||||
OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
|
||||
return new OrderedHiddenHttpMethodFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
OrderedFormContentFilter formContentFilter() {
|
||||
return new OrderedFormContentFilter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user