Skip to content

Commit 1270af9

Browse files
committed
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
1 parent 980ddcf commit 1270af9

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.Format;
5757
import org.springframework.boot.context.properties.EnableConfigurationProperties;
5858
import org.springframework.boot.convert.ApplicationConversionService;
59+
import org.springframework.boot.web.servlet.ServletRegistrationBean;
5960
import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter;
6061
import org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter;
6162
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
@@ -191,18 +192,22 @@ public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
191192

192193
private final ObjectProvider<DispatcherServletPath> dispatcherServletPath;
193194

195+
private final ObjectProvider<ServletRegistrationBean<?>> servletRegistrations;
196+
194197
final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
195198

196199
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
197200
ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
198201
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
199-
ObjectProvider<DispatcherServletPath> dispatcherServletPath) {
202+
ObjectProvider<DispatcherServletPath> dispatcherServletPath,
203+
ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
200204
this.resourceProperties = resourceProperties;
201205
this.mvcProperties = mvcProperties;
202206
this.beanFactory = beanFactory;
203207
this.messageConvertersProvider = messageConvertersProvider;
204208
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
205209
this.dispatcherServletPath = dispatcherServletPath;
210+
this.servletRegistrations = servletRegistrations;
206211
}
207212

208213
@Override
@@ -234,14 +239,19 @@ public void configurePathMatch(PathMatchConfigurer configurer) {
234239
this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());
235240
this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {
236241
String servletUrlMapping = dispatcherPath.getServletUrlMapping();
237-
if (servletUrlMapping.equals("/")) {
242+
if (servletUrlMapping.equals("/") && singleDispatcherServlet()) {
238243
UrlPathHelper urlPathHelper = new UrlPathHelper();
239244
urlPathHelper.setAlwaysUseFullPath(true);
240245
configurer.setUrlPathHelper(urlPathHelper);
241246
}
242247
});
243248
}
244249

250+
private boolean singleDispatcherServlet() {
251+
return this.servletRegistrations.stream().map(ServletRegistrationBean::getServlet)
252+
.filter(DispatcherServlet.class::isInstance).count() == 1;
253+
}
254+
245255
@Override
246256
@SuppressWarnings("deprecation")
247257
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
5353
import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor;
5454
import org.springframework.boot.web.servlet.FilterRegistrationBean;
55+
import org.springframework.boot.web.servlet.ServletRegistrationBean;
5556
import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter;
5657
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
5758
import org.springframework.context.ApplicationContext;
@@ -84,6 +85,7 @@
8485
import org.springframework.web.filter.FormContentFilter;
8586
import org.springframework.web.filter.HiddenHttpMethodFilter;
8687
import org.springframework.web.filter.RequestContextFilter;
88+
import org.springframework.web.servlet.DispatcherServlet;
8789
import org.springframework.web.servlet.HandlerAdapter;
8890
import org.springframework.web.servlet.HandlerExceptionResolver;
8991
import org.springframework.web.servlet.HandlerMapping;
@@ -866,6 +868,23 @@ void urlPathHelperDoesNotUseFullPathWithServletMapping() {
866868
});
867869
}
868870

871+
@Test
872+
void urlPathHelperDoesNotUseFullPathWithAdditionalDispatcherServlet() {
873+
this.contextRunner.withUserConfiguration(AdditionalDispatcherServletConfiguration.class).run((context) -> {
874+
UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class);
875+
assertThat(urlPathHelper).extracting("alwaysUseFullPath").isEqualTo(false);
876+
});
877+
}
878+
879+
@Test
880+
void urlPathHelperDoesNotUseFullPathWithAdditionalUntypedDispatcherServlet() {
881+
this.contextRunner.withUserConfiguration(AdditionalUntypedDispatcherServletConfiguration.class)
882+
.run((context) -> {
883+
UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class);
884+
assertThat(urlPathHelper).extracting("alwaysUseFullPath").isEqualTo(false);
885+
});
886+
}
887+
869888
private void assertCacheControl(AssertableWebApplicationContext context) {
870889
Map<String, Object> handlerMap = getHandlerMap(context.getBean("resourceHandlerMapping", HandlerMapping.class));
871890
assertThat(handlerMap).hasSize(2);
@@ -1248,4 +1267,24 @@ public void addCorsMappings(CorsRegistry registry) {
12481267

12491268
}
12501269

1270+
@Configuration(proxyBeanMethods = false)
1271+
static class AdditionalDispatcherServletConfiguration {
1272+
1273+
@Bean
1274+
ServletRegistrationBean<DispatcherServlet> additionalDispatcherServlet() {
1275+
return new ServletRegistrationBean<>(new DispatcherServlet());
1276+
}
1277+
1278+
}
1279+
1280+
@Configuration(proxyBeanMethods = false)
1281+
static class AdditionalUntypedDispatcherServletConfiguration {
1282+
1283+
@Bean
1284+
ServletRegistrationBean<?> additionalDispatcherServlet() {
1285+
return new ServletRegistrationBean<>(new DispatcherServlet());
1286+
}
1287+
1288+
}
1289+
12511290
}

0 commit comments

Comments
 (0)