Commit 1270af90 authored by Andy Wilkinson's avatar Andy Wilkinson

Only enable full path optimization when there's one DispatcherServlet

Previously, UrlPathHelper's full path optimization was enabled when
there was a dispatcher servlet mapped to /. The UrlPathHelper is used
across Spring MVC and if there are multiple dispatcher servlets they
all share the sample UrlPathHelper. This meant that any additional
dispatcher servlets mapping to locations other than / would not be able
to map requests correctly as the UrlPathHelper would use the full path,
ignoring the url mapping of the dispatcher servlet.

This commit updates the MVC auto-configuration so that use of the full
path is only enabled if there's a single dispatcher servlet
registration.

Fixes gh-22682
parent 980ddcff
......@@ -56,6 +56,7 @@ import org.springframework.boot.autoconfigure.web.format.WebConversionService;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.Format;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter;
import org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter;
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
......@@ -191,18 +192,22 @@ public class WebMvcAutoConfiguration {
private final ObjectProvider<DispatcherServletPath> dispatcherServletPath;
private final ObjectProvider<ServletRegistrationBean<?>> servletRegistrations;
final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
ObjectProvider<DispatcherServletPath> dispatcherServletPath) {
ObjectProvider<DispatcherServletPath> dispatcherServletPath,
ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.dispatcherServletPath = dispatcherServletPath;
this.servletRegistrations = servletRegistrations;
}
@Override
......@@ -234,7 +239,7 @@ public class WebMvcAutoConfiguration {
this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());
this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {
String servletUrlMapping = dispatcherPath.getServletUrlMapping();
if (servletUrlMapping.equals("/")) {
if (servletUrlMapping.equals("/") && singleDispatcherServlet()) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setAlwaysUseFullPath(true);
configurer.setUrlPathHelper(urlPathHelper);
......@@ -242,6 +247,11 @@ public class WebMvcAutoConfiguration {
});
}
private boolean singleDispatcherServlet() {
return this.servletRegistrations.stream().map(ServletRegistrationBean::getServlet)
.filter(DispatcherServlet.class::isInstance).count() == 1;
}
@Override
@SuppressWarnings("deprecation")
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
......
......@@ -52,6 +52,7 @@ import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationContext;
......@@ -84,6 +85,7 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.FormContentFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.filter.RequestContextFilter;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerMapping;
......@@ -866,6 +868,23 @@ class WebMvcAutoConfigurationTests {
});
}
@Test
void urlPathHelperDoesNotUseFullPathWithAdditionalDispatcherServlet() {
this.contextRunner.withUserConfiguration(AdditionalDispatcherServletConfiguration.class).run((context) -> {
UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class);
assertThat(urlPathHelper).extracting("alwaysUseFullPath").isEqualTo(false);
});
}
@Test
void urlPathHelperDoesNotUseFullPathWithAdditionalUntypedDispatcherServlet() {
this.contextRunner.withUserConfiguration(AdditionalUntypedDispatcherServletConfiguration.class)
.run((context) -> {
UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class);
assertThat(urlPathHelper).extracting("alwaysUseFullPath").isEqualTo(false);
});
}
private void assertCacheControl(AssertableWebApplicationContext context) {
Map<String, Object> handlerMap = getHandlerMap(context.getBean("resourceHandlerMapping", HandlerMapping.class));
assertThat(handlerMap).hasSize(2);
......@@ -1248,4 +1267,24 @@ class WebMvcAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class AdditionalDispatcherServletConfiguration {
@Bean
ServletRegistrationBean<DispatcherServlet> additionalDispatcherServlet() {
return new ServletRegistrationBean<>(new DispatcherServlet());
}
}
@Configuration(proxyBeanMethods = false)
static class AdditionalUntypedDispatcherServletConfiguration {
@Bean
ServletRegistrationBean<?> additionalDispatcherServlet() {
return new ServletRegistrationBean<>(new DispatcherServlet());
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment