Move server.servlet.path to spring.mvc.servlet.path

Closes gh-12971
This commit is contained in:
Stephane Nicoll 2018-05-14 12:24:38 +02:00
parent 406192741e
commit 93c45cb6ec
15 changed files with 172 additions and 132 deletions

View File

@ -27,7 +27,7 @@ import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.security.StaticResourceLocation;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
@ -96,14 +96,14 @@ public final class StaticResourceRequest {
* Locations}.
*/
public static final class StaticResourceRequestMatcher
extends ApplicationContextRequestMatcher<ServerProperties> {
extends ApplicationContextRequestMatcher<WebMvcProperties> {
private final Set<StaticResourceLocation> locations;
private volatile RequestMatcher delegate;
private StaticResourceRequestMatcher(Set<StaticResourceLocation> locations) {
super(ServerProperties.class);
super(WebMvcProperties.class);
this.locations = locations;
}
@ -134,25 +134,25 @@ public final class StaticResourceRequest {
}
@Override
protected void initialized(Supplier<ServerProperties> serverProperties) {
protected void initialized(Supplier<WebMvcProperties> serverProperties) {
this.delegate = new OrRequestMatcher(
getDelegateMatchers(serverProperties.get()));
}
private List<RequestMatcher> getDelegateMatchers(
ServerProperties serverProperties) {
WebMvcProperties serverProperties) {
return getPatterns(serverProperties).map(AntPathRequestMatcher::new)
.collect(Collectors.toList());
}
private Stream<String> getPatterns(ServerProperties serverProperties) {
private Stream<String> getPatterns(WebMvcProperties serverProperties) {
return this.locations.stream().flatMap(StaticResourceLocation::getPatterns)
.map(serverProperties.getServlet()::getPath);
}
@Override
protected boolean matches(HttpServletRequest request,
Supplier<ServerProperties> context) {
Supplier<WebMvcProperties> context) {
return this.delegate.matches(request);
}

View File

@ -22,7 +22,6 @@ import java.nio.charset.Charset;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@ -37,7 +36,6 @@ import org.springframework.boot.web.server.Http2;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.servlet.server.Jsp;
import org.springframework.boot.web.servlet.server.Session;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@ -214,11 +212,6 @@ public class ServerProperties {
*/
private String applicationDisplayName = "application";
/**
* Path of the main dispatcher servlet.
*/
private String path = "/";
@NestedConfigurationProperty
private final Jsp jsp = new Jsp();
@ -248,15 +241,6 @@ public class ServerProperties {
this.applicationDisplayName = displayName;
}
public String getPath() {
return this.path;
}
public void setPath(String path) {
Assert.notNull(path, "Path must not be null");
this.path = path;
}
public Map<String, String> getContextParameters() {
return this.contextParameters;
}
@ -269,57 +253,6 @@ public class ServerProperties {
return this.session;
}
public String getServletMapping() {
if (this.path.equals("") || this.path.equals("/")) {
return "/";
}
if (this.path.contains("*")) {
return this.path;
}
if (this.path.endsWith("/")) {
return this.path + "*";
}
return this.path + "/*";
}
public String getPath(String path) {
String prefix = getServletPrefix();
if (!path.startsWith("/")) {
path = "/" + path;
}
return prefix + path;
}
public String getServletPrefix() {
String result = this.path;
int index = result.indexOf('*');
if (index != -1) {
result = result.substring(0, index);
}
if (result.endsWith("/")) {
result = result.substring(0, result.length() - 1);
}
return result;
}
public String[] getPathsArray(Collection<String> paths) {
String[] result = new String[paths.size()];
int i = 0;
for (String path : paths) {
result[i++] = getPath(path);
}
return result;
}
public String[] getPathsArray(String[] paths) {
String[] result = new String[paths.length];
int i = 0;
for (String path : paths) {
result[i++] = getPath(path);
}
return result;
}
}
/**

View File

@ -36,7 +36,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@ -67,7 +66,6 @@ import org.springframework.web.servlet.DispatcherServlet;
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
@EnableConfigurationProperties(ServerProperties.class)
public class DispatcherServletAutoConfiguration {
/*
@ -88,12 +86,8 @@ public class DispatcherServletAutoConfiguration {
private final WebMvcProperties webMvcProperties;
private final ServerProperties serverProperties;
public DispatcherServletConfiguration(WebMvcProperties webMvcProperties,
ServerProperties serverProperties) {
public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {
this.webMvcProperties = webMvcProperties;
this.serverProperties = serverProperties;
}
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
@ -118,7 +112,7 @@ public class DispatcherServletAutoConfiguration {
@Bean
public DispatcherServletPathProvider mainDispatcherServletPathProvider() {
return () -> DispatcherServletConfiguration.this.serverProperties.getServlet()
return () -> DispatcherServletConfiguration.this.webMvcProperties.getServlet()
.getPath();
}
@ -131,16 +125,13 @@ public class DispatcherServletAutoConfiguration {
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
private final ServerProperties serverProperties;
private final WebMvcProperties webMvcProperties;
private final MultipartConfigElement multipartConfig;
public DispatcherServletRegistrationConfiguration(
ServerProperties serverProperties, WebMvcProperties webMvcProperties,
WebMvcProperties webMvcProperties,
ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
this.serverProperties = serverProperties;
this.webMvcProperties = webMvcProperties;
this.multipartConfig = multipartConfigProvider.getIfAvailable();
}
@ -151,7 +142,7 @@ public class DispatcherServletAutoConfiguration {
DispatcherServlet dispatcherServlet) {
ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(
dispatcherServlet,
this.serverProperties.getServlet().getServletMapping());
this.webMvcProperties.getServlet().getServletMapping());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup());

View File

@ -23,6 +23,7 @@ import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.validation.DefaultMessageCodesResolver;
/**
@ -225,11 +226,25 @@ public class WebMvcProperties {
public static class Servlet {
/**
* Path of the dispatcher servlet.
*/
private String path = "/";
/**
* Load on startup priority of the dispatcher servlet.
*/
private int loadOnStartup = -1;
public String getPath() {
return this.path;
}
public void setPath(String path) {
Assert.notNull(path, "Path must not be null");
this.path = path;
}
public int getLoadOnStartup() {
return this.loadOnStartup;
}
@ -238,6 +253,39 @@ public class WebMvcProperties {
this.loadOnStartup = loadOnStartup;
}
public String getServletMapping() {
if (this.path.equals("") || this.path.equals("/")) {
return "/";
}
if (this.path.contains("*")) {
return this.path;
}
if (this.path.endsWith("/")) {
return this.path + "*";
}
return this.path + "/*";
}
public String getPath(String path) {
String prefix = getServletPrefix();
if (!path.startsWith("/")) {
path = "/" + path;
}
return prefix + path;
}
public String getServletPrefix() {
String result = this.path;
int index = result.indexOf('*');
if (index != -1) {
result = result.substring(0, index);
}
if (result.endsWith("/")) {
result = result.substring(0, result.length() - 1);
}
return result;
}
}
public static class View {

View File

@ -50,6 +50,7 @@ import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvi
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
@ -89,16 +90,21 @@ import org.springframework.web.util.HtmlUtils;
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
// Load before the main WebMvcAutoConfiguration so that the error View is available
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class })
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class,
WebMvcProperties.class })
public class ErrorMvcAutoConfiguration {
private final ServerProperties serverProperties;
private final WebMvcProperties webMvcProperties;
private final List<ErrorViewResolver> errorViewResolvers;
public ErrorMvcAutoConfiguration(ServerProperties serverProperties,
WebMvcProperties webMvcProperties,
ObjectProvider<List<ErrorViewResolver>> errorViewResolversProvider) {
this.serverProperties = serverProperties;
this.webMvcProperties = webMvcProperties;
this.errorViewResolvers = errorViewResolversProvider.getIfAvailable();
}
@ -118,7 +124,7 @@ public class ErrorMvcAutoConfiguration {
@Bean
public ErrorPageCustomizer errorPageCustomizer() {
return new ErrorPageCustomizer(this.serverProperties);
return new ErrorPageCustomizer(this.serverProperties, this.webMvcProperties);
}
@Bean
@ -325,17 +331,21 @@ public class ErrorMvcAutoConfiguration {
*/
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
private final ServerProperties properties;
private final ServerProperties serverProperties;
protected ErrorPageCustomizer(ServerProperties properties) {
this.properties = properties;
private final WebMvcProperties webMvcProperties;
protected ErrorPageCustomizer(ServerProperties serverProperties,
WebMvcProperties webMvcProperties) {
this.serverProperties = serverProperties;
this.webMvcProperties = webMvcProperties;
}
@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
ErrorPage errorPage = new ErrorPage(
this.properties.getServlet().getServletPrefix()
+ this.properties.getError().getPath());
this.webMvcProperties.getServlet().getServletPrefix()
+ this.serverProperties.getError().getPath());
errorPageRegistry.addErrorPages(errorPage);
}

View File

@ -1069,7 +1069,17 @@
"description": "Path of the main dispatcher servlet.",
"defaultValue": "/",
"deprecation": {
"replacement": "server.servlet.path",
"replacement": "spring.mvc.servlet.path",
"level": "error"
}
},
{
"name": "server.servlet.path",
"type": "java.lang.String",
"description": "Path of the main dispatcher servlet.",
"defaultValue": "/",
"deprecation": {
"replacement": "spring.mvc.servlet.path",
"level": "error"
}
},

View File

@ -24,7 +24,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.autoconfigure.security.StaticResourceLocation;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.util.matcher.RequestMatcher;
@ -74,11 +74,11 @@ public class StaticResourceRequestTests {
@Test
public void atLocationWhenHasServletPathShouldMatchLocation() {
ServerProperties serverProperties = new ServerProperties();
serverProperties.getServlet().setPath("/foo");
WebMvcProperties webMvcProperties = new WebMvcProperties();
webMvcProperties.getServlet().setPath("/foo");
RequestMatcher matcher = this.resourceRequest.at(StaticResourceLocation.CSS);
assertMatcher(matcher, serverProperties).matches("/foo", "/css/file.css");
assertMatcher(matcher, serverProperties).doesNotMatch("/foo", "/js/file.js");
assertMatcher(matcher, webMvcProperties).matches("/foo", "/css/file.css");
assertMatcher(matcher, webMvcProperties).doesNotMatch("/foo", "/js/file.js");
}
@Test
@ -97,14 +97,14 @@ public class StaticResourceRequestTests {
private RequestMatcherAssert assertMatcher(RequestMatcher matcher) {
StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerBean(ServerProperties.class);
context.registerBean(WebMvcProperties.class);
return assertThat(new RequestMatcherAssert(context, matcher));
}
private RequestMatcherAssert assertMatcher(RequestMatcher matcher,
ServerProperties serverProperties) {
WebMvcProperties webMvcProperties) {
StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerBean(ServerProperties.class, () -> serverProperties);
context.registerBean(WebMvcProperties.class, () -> webMvcProperties);
return assertThat(new RequestMatcherAssert(context, matcher));
}

View File

@ -78,20 +78,6 @@ public class ServerPropertiesTests {
.isEqualTo(Duration.ofMillis(60000));
}
@Test
public void testServletPathAsMapping() {
bind("server.servlet.path", "/foo/*");
assertThat(this.properties.getServlet().getServletMapping()).isEqualTo("/foo/*");
assertThat(this.properties.getServlet().getServletPrefix()).isEqualTo("/foo");
}
@Test
public void testServletPathAsPrefix() {
bind("server.servlet.path", "/foo");
assertThat(this.properties.getServlet().getServletMapping()).isEqualTo("/foo/*");
assertThat(this.properties.getServlet().getServletPrefix()).isEqualTo("/foo");
}
@Test
public void testTomcatBinding() {
Map<String, String> map = new HashMap<>();

View File

@ -99,7 +99,7 @@ public class DispatcherServletAutoConfigurationTests {
@Test
public void servletPath() {
this.contextRunner.withPropertyValues("server.servlet.path:/spring")
this.contextRunner.withPropertyValues("spring.mvc.servlet.path:/spring")
.run((context) -> {
assertThat(context.getBean(DispatcherServlet.class)).isNotNull();
ServletRegistrationBean<?> registration = context

View File

@ -0,0 +1,63 @@
/*
* Copyright 2012-2018 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
*
* http://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.web.servlet;
import java.util.Collections;
import java.util.Map;
import org.junit.Test;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link WebMvcProperties}.
*
* @author Stephane Nicoll
*/
public class WebMvcPropertiesTests {
private final WebMvcProperties properties = new WebMvcProperties();
@Test
public void testServletPathAsMapping() {
bind("spring.mvc.servlet.path", "/foo/*");
assertThat(this.properties.getServlet().getServletMapping()).isEqualTo("/foo/*");
assertThat(this.properties.getServlet().getServletPrefix()).isEqualTo("/foo");
}
@Test
public void testServletPathAsPrefix() {
bind("spring.mvc.servlet.path", "/foo");
assertThat(this.properties.getServlet().getServletMapping()).isEqualTo("/foo/*");
assertThat(this.properties.getServlet().getServletPrefix()).isEqualTo("/foo");
}
private void bind(String name, String value) {
bind(Collections.singletonMap(name, value));
}
private void bind(Map<String, String> map) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(map);
new Binder(source).bind("spring.mvc", Bindable.ofInstance(this.properties));
}
}

View File

@ -47,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "server.servlet.path:/spring/*")
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.mvc.servlet.path:/spring/*")
@DirtiesContext
public class RemappedErrorViewIntegrationTests {
@ -93,7 +93,7 @@ public class RemappedErrorViewIntegrationTests {
// For manual testing
public static void main(String[] args) {
new SpringApplicationBuilder(TestConfiguration.class)
.properties("server.servlet.path:spring/*").run(args);
.properties("spring.mvc.servlet.path:spring/*").run(args);
}
}

View File

@ -196,7 +196,6 @@ content into your application. Rather, pick only the properties that you need.
server.servlet.jsp.class-name=org.apache.jasper.servlet.JspServlet # The class name of the JSP servlet.
server.servlet.jsp.init-parameters.*= # Init parameters used to configure the JSP servlet.
server.servlet.jsp.registered=true # Whether the JSP servlet is registered.
server.servlet.path=/ # Path of the main dispatcher servlet.
server.servlet.session.cookie.comment= # Comment for the session cookie.
server.servlet.session.cookie.domain= # Domain for the session cookie.
server.servlet.session.cookie.http-only= # "HttpOnly" flag for the session cookie.
@ -414,6 +413,7 @@ content into your application. Rather, pick only the properties that you need.
spring.mvc.pathmatch.use-registered-suffix-pattern=false # Whether suffix pattern matching should work only against extensions registered with "spring.mvc.contentnegotiation.media-types.*".
spring.mvc.pathmatch.use-suffix-pattern=false # Whether to use suffix pattern match (".*") when matching patterns to requests.
spring.mvc.servlet.load-on-startup=-1 # Load on startup priority of the dispatcher servlet.
spring.mvc.servlet.path=/ # Path of the dispatcher servlet.
spring.mvc.static-path-pattern=/** # Path pattern used for static resources.
spring.mvc.throw-exception-if-no-handler-found=false # Whether a "NoHandlerFoundException" should be thrown if no Handler was found to process a request.
spring.mvc.view.prefix= # Spring MVC view prefix.

View File

@ -42,10 +42,9 @@ categorized under "hints", as shown in the following example:
"sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
},
{
"name": "server.servlet.path",
"type": "java.lang.String",
"sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties",
"defaultValue": "/"
"name": "server.address",
"type": "java.net.InetAddress",
"sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
},
{
"name": "spring.jpa.hibernate.ddl-auto",
@ -84,18 +83,18 @@ categorized under "hints", as shown in the following example:
----
Each "`property`" is a configuration item that the user specifies with a given value.
For example, `server.port` and `server.servlet.path` might be specified in
For example, `server.port` and `server.address` might be specified in
`application.properties`, as follows:
[source,properties,indent=0]
----
server.port=9090
server.servlet.path=/home
server.address=127.0.0.1
----
The "`groups`" are higher level items that do not themselves specify a value but instead
provide a contextual grouping for properties. For example, the `server.port` and
`server.servlet.path` properties are part of the `server` group.
`server.address` properties are part of the `server` group.
NOTE: It is not required that every "`property`" has a "`group`". Some properties might
exist in their own right.
@ -163,7 +162,7 @@ in the following table:
|`name`
| String
| The full name of the property. Names are in lower-case period-separated form (for
example, `server.servlet.path`). This attribute is mandatory.
example, `server.address`). This attribute is mandatory.
|`type`
| String
@ -287,7 +286,7 @@ following table:
|`name`
| String
| The full name of the property to which this hint refers. Names are in lower-case
period-separated form (such as `server.servlet.path`). If the property refers to a map
period-separated form (such as `spring.mvc.servlet.path`). If the property refers to a map
(such as `system.contexts`), the hint either applies to the _keys_ of the map
(`system.context.keys`) or the _values_ (`system.context.values`) of the map. This
attribute is mandatory.

View File

@ -38,7 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"server.servlet.path=/spring" })
"spring.mvc.servlet.path=/spring" })
public class ServletPathSampleActuatorApplicationTests {
@Autowired

View File

@ -1 +1 @@
server.servlet.path=/home/*
spring.mvc.servlet.path=/home/*