Adapt to change in default strategy for URL path matching in Spring MVC

Closes gh-31547
pull/31699/head
Madhura Bhave 2 years ago
parent f015095ca0
commit 7954f5e566

@ -37,7 +37,6 @@ import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.Link; import org.springframework.boot.actuate.endpoint.web.Link;
import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.WebOperation;
import org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -64,8 +63,7 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin
Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes, Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor, CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor,
EndpointLinksResolver linksResolver) { EndpointLinksResolver linksResolver) {
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true, super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true);
WebMvcAutoConfiguration.pathPatternParser);
this.securityInterceptor = securityInterceptor; this.securityInterceptor = securityInterceptor;
this.linksResolver = linksResolver; this.linksResolver = linksResolver;
} }

@ -47,7 +47,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
@ -85,7 +84,7 @@ public class WebMvcEndpointManagementContextConfiguration {
boolean shouldRegisterLinksMapping = shouldRegisterLinksMapping(webEndpointProperties, environment, basePath); boolean shouldRegisterLinksMapping = shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath),
shouldRegisterLinksMapping, WebMvcAutoConfiguration.pathPatternParser); shouldRegisterLinksMapping);
} }
private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment,

@ -55,6 +55,7 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder; import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer; import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.both;
@ -86,7 +87,7 @@ class WebMvcEndpointIntegrationTests {
this.context.setServletContext(new MockServletContext()); this.context.setServletContext(new MockServletContext());
this.context.refresh(); this.context.refresh();
WebMvcEndpointHandlerMapping handlerMapping = this.context.getBean(WebMvcEndpointHandlerMapping.class); WebMvcEndpointHandlerMapping handlerMapping = this.context.getBean(WebMvcEndpointHandlerMapping.class);
assertThat(handlerMapping.getPatternParser()).isEqualTo(WebMvcAutoConfiguration.pathPatternParser); assertThat(handlerMapping.getPatternParser()).isInstanceOf(PathPatternParser.class);
} }
@Test @Test

@ -128,29 +128,11 @@ public abstract class AbstractWebMvcEndpointHandlerMapping extends RequestMappin
public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes, Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
CorsConfiguration corsConfiguration, boolean shouldRegisterLinksMapping) { CorsConfiguration corsConfiguration, boolean shouldRegisterLinksMapping) {
this(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, shouldRegisterLinksMapping, null);
}
/**
* Creates a new {@code AbstractWebMvcEndpointHandlerMapping} that provides mappings
* for the operations of the given endpoints.
* @param endpointMapping the base mapping for all endpoints
* @param endpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
* @param corsConfiguration the CORS configuration for the endpoints or {@code null}
* @param shouldRegisterLinksMapping whether the links endpoint should be registered
* @param pathPatternParser the path pattern parser
*/
public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
CorsConfiguration corsConfiguration, boolean shouldRegisterLinksMapping,
PathPatternParser pathPatternParser) {
this.endpointMapping = endpointMapping; this.endpointMapping = endpointMapping;
this.endpoints = endpoints; this.endpoints = endpoints;
this.endpointMediaTypes = endpointMediaTypes; this.endpointMediaTypes = endpointMediaTypes;
this.corsConfiguration = corsConfiguration; this.corsConfiguration = corsConfiguration;
this.shouldRegisterLinksMapping = shouldRegisterLinksMapping; this.shouldRegisterLinksMapping = shouldRegisterLinksMapping;
setPatternParser(pathPatternParser);
setOrder(-100); setOrder(-100);
} }

@ -31,7 +31,6 @@ import org.springframework.boot.actuate.endpoint.web.Link;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.pattern.PathPatternParser;
/** /**
* A custom {@link HandlerMapping} that makes web endpoints available over HTTP using * A custom {@link HandlerMapping} that makes web endpoints available over HTTP using
@ -63,27 +62,6 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM
setOrder(-100); setOrder(-100);
} }
/**
* Creates a new {@code WebMvcEndpointHandlerMapping} instance that provides mappings
* for the given endpoints.
* @param endpointMapping the base mapping for all endpoints
* @param endpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
* @param corsConfiguration the CORS configuration for the endpoints or {@code null}
* @param linksResolver resolver for determining links to available endpoints
* @param shouldRegisterLinksMapping whether the links endpoint should be registered
* @param pathPatternParser the path pattern parser
*/
public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, Collection<ExposableWebEndpoint> endpoints,
EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration,
EndpointLinksResolver linksResolver, boolean shouldRegisterLinksMapping,
PathPatternParser pathPatternParser) {
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, shouldRegisterLinksMapping,
pathPatternParser);
this.linksResolver = linksResolver;
setOrder(-100);
}
@Override @Override
protected LinksHandler getLinksHandler() { protected LinksHandler getLinksHandler() {
return new WebMvcLinksHandler(); return new WebMvcLinksHandler();

@ -75,6 +75,7 @@ import org.springframework.format.FormatterRegistry;
import org.springframework.format.support.FormattingConversionService; import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.validation.DefaultMessageCodesResolver; import org.springframework.validation.DefaultMessageCodesResolver;
import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.MessageCodesResolver;
@ -250,20 +251,20 @@ public class WebMvcAutoConfiguration {
@Override @Override
public void configurePathMatch(PathMatchConfigurer configurer) { public void configurePathMatch(PathMatchConfigurer configurer) {
if (this.mvcProperties.getPathmatch() if (this.mvcProperties.getPathmatch()
.getMatchingStrategy() == WebMvcProperties.MatchingStrategy.PATH_PATTERN_PARSER) { .getMatchingStrategy() == WebMvcProperties.MatchingStrategy.ANT_PATH_MATCHER) {
configurer.setPatternParser(pathPatternParser); configurer.setPathMatcher(new AntPathMatcher());
configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());
configurer.setUseRegisteredSuffixPatternMatch(
this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());
this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {
String servletUrlMapping = dispatcherPath.getServletUrlMapping();
if (servletUrlMapping.equals("/") && singleDispatcherServlet()) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setAlwaysUseFullPath(true);
configurer.setUrlPathHelper(urlPathHelper);
}
});
} }
configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());
configurer.setUseRegisteredSuffixPatternMatch(
this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());
this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {
String servletUrlMapping = dispatcherPath.getServletUrlMapping();
if (servletUrlMapping.equals("/") && singleDispatcherServlet()) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setAlwaysUseFullPath(true);
configurer.setUrlPathHelper(urlPathHelper);
}
});
} }
private boolean singleDispatcherServlet() { private boolean singleDispatcherServlet() {

@ -958,19 +958,21 @@ class WebMvcAutoConfigurationTests {
} }
@Test @Test
void urlPathHelperUsesFullPathByDefault() { void urlPathHelperUsesFullPathByDefaultWhenAntPathMatchingIsUsed() {
this.contextRunner.run((context) -> { this.contextRunner.withPropertyValues("spring.mvc.pathmatch.matching-strategy:ant-path-matcher")
UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class); .run((context) -> {
assertThat(urlPathHelper).extracting("alwaysUseFullPath").isEqualTo(true); UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class);
}); assertThat(urlPathHelper).extracting("alwaysUseFullPath").isEqualTo(true);
});
} }
@Test @Test
void urlPathHelperDoesNotUseFullPathWithServletMapping() { void urlPathHelperDoesNotUseFullPathWithServletMapping() {
this.contextRunner.withPropertyValues("spring.mvc.servlet.path=/test/").run((context) -> { this.contextRunner.withPropertyValues("spring.mvc.pathmatch.matching-strategy:ant-path-matcher")
UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class); .withPropertyValues("spring.mvc.servlet.path=/test/").run((context) -> {
assertThat(urlPathHelper).extracting("alwaysUseFullPath").isEqualTo(false); UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class);
}); assertThat(urlPathHelper).extracting("alwaysUseFullPath").isEqualTo(false);
});
} }
@Test @Test

Loading…
Cancel
Save