Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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 spring-projectsgh-22682
  • Loading branch information
wilkinsona committed Aug 11, 2020
1 parent 980ddcf commit 1270af9
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 2 deletions.
Expand Up @@ -56,6 +56,7 @@
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;
Expand Down Expand Up @@ -191,18 +192,22 @@ public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

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
Expand Down Expand Up @@ -234,14 +239,19 @@ public void configurePathMatch(PathMatchConfigurer configurer) {
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);
}
});
}

private boolean singleDispatcherServlet() {
return this.servletRegistrations.stream().map(ServletRegistrationBean::getServlet)
.filter(DispatcherServlet.class::isInstance).count() == 1;
}

@Override
@SuppressWarnings("deprecation")
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Expand Down
Expand Up @@ -52,6 +52,7 @@
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;
Expand Down Expand Up @@ -84,6 +85,7 @@
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;
Expand Down Expand Up @@ -866,6 +868,23 @@ void urlPathHelperDoesNotUseFullPathWithServletMapping() {
});
}

@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);
Expand Down Expand Up @@ -1248,4 +1267,24 @@ public void addCorsMappings(CorsRegistry registry) {

}

@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());
}

}

}

0 comments on commit 1270af9

Please sign in to comment.