Skip to content

Commit

Permalink
Enable use of parsed patterns by default in Spring MVC
Browse files Browse the repository at this point in the history
Closes gh-28607
  • Loading branch information
rstoyanchev committed Jun 29, 2022
1 parent 8a9b082 commit 92cf1e1
Show file tree
Hide file tree
Showing 16 changed files with 234 additions and 146 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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.
Expand Down Expand Up @@ -127,6 +127,8 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
@Nullable
private FlashMapManager flashMapManager;

private boolean preferPathMatcher = false;

@Nullable
private PathPatternParser patternParser;

Expand Down Expand Up @@ -317,8 +319,10 @@ public StandaloneMockMvcBuilder setFlashMapManager(FlashMapManager flashMapManag
* @param parser the parser to use
* @since 5.3
*/
public void setPatternParser(PathPatternParser parser) {
public StandaloneMockMvcBuilder setPatternParser(@Nullable PathPatternParser parser) {
this.patternParser = parser;
this.preferPathMatcher = (this.patternParser == null);
return this;
}

/**
Expand All @@ -332,6 +336,7 @@ public void setPatternParser(PathPatternParser parser) {
@Deprecated
public StandaloneMockMvcBuilder setUseSuffixPatternMatch(boolean useSuffixPatternMatch) {
this.useSuffixPatternMatch = useSuffixPatternMatch;
this.preferPathMatcher |= useSuffixPatternMatch;
return this;
}

Expand Down Expand Up @@ -468,15 +473,16 @@ public RequestMappingHandlerMapping getHandlerMapping(

RequestMappingHandlerMapping handlerMapping = handlerMappingFactory.get();
handlerMapping.setEmbeddedValueResolver(new StaticStringValueResolver(placeholderValues));
if (patternParser != null) {
handlerMapping.setPatternParser(patternParser);
}
else {
if (patternParser == null && preferPathMatcher) {
handlerMapping.setPatternParser(null);
handlerMapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
if (removeSemicolonContent != null) {
handlerMapping.setRemoveSemicolonContent(removeSemicolonContent);
}
}
else if (patternParser != null) {
handlerMapping.setPatternParser(patternParser);
}
handlerMapping.setUseTrailingSlashMatch(useTrailingSlashPatternMatch);
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 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.
Expand Down Expand Up @@ -53,7 +53,7 @@
*/
public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource {

private static PathMatcher defaultPathMatcher = new AntPathMatcher();
private static final PathMatcher defaultPathMatcher = new AntPathMatcher();


private final PathPatternParser patternParser;
Expand Down Expand Up @@ -157,7 +157,7 @@ public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
* pattern matching with {@link PathMatcher} or with parsed {@link PathPattern}s.
* <p>In Spring MVC, either a resolved String lookupPath or a parsed
* {@code RequestPath} is always available within {@code DispatcherServlet}
* processing. However in a Servlet {@code Filter} such as {@code CorsFilter}
* processing. However, in a Servlet {@code Filter} such as {@code CorsFilter}
* that may or may not be the case.
* <p>By default this is set to {@code true} in which case lazy lookupPath
* initialization is allowed. Set this to {@code false} when an
Expand Down
Expand Up @@ -252,6 +252,9 @@ public static RequestPath parse(HttpServletRequest request) {
if (UrlPathHelper.servlet4Present) {
String servletPathPrefix = Servlet4Delegate.getServletPathPrefix(request);
if (StringUtils.hasText(servletPathPrefix)) {
if (servletPathPrefix.endsWith("/")) {
servletPathPrefix = servletPathPrefix.substring(0, servletPathPrefix.length() - 1);
}
return new ServletRequestPath(requestUri, request.getContextPath(), servletPathPrefix);
}
}
Expand All @@ -272,8 +275,7 @@ public static String getServletPathPrefix(HttpServletRequest request) {
if (mapping == null) {
mapping = request.getHttpServletMapping();
}
MappingMatch match = mapping.getMappingMatch();
if (!ObjectUtils.nullSafeEquals(match, MappingMatch.PATH)) {
if (!ObjectUtils.nullSafeEquals(mapping.getMappingMatch(), MappingMatch.PATH)) {
return null;
}
String servletPath = (String) request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE);
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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.
Expand Down Expand Up @@ -405,33 +405,45 @@ private void configurePathMatchingProperties(
if (pathMatchingElement != null) {
Object source = context.extractSource(element);

if (pathMatchingElement.hasAttribute("suffix-pattern")) {
Boolean useSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("suffix-pattern"));
handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch);
}
if (pathMatchingElement.hasAttribute("trailing-slash")) {
Boolean useTrailingSlashMatch = Boolean.valueOf(pathMatchingElement.getAttribute("trailing-slash"));
boolean useTrailingSlashMatch = Boolean.parseBoolean(pathMatchingElement.getAttribute("trailing-slash"));
handlerMappingDef.getPropertyValues().add("useTrailingSlashMatch", useTrailingSlashMatch);
}

boolean preferPathMatcher = false;

if (pathMatchingElement.hasAttribute("suffix-pattern")) {
boolean useSuffixPatternMatch = Boolean.parseBoolean(pathMatchingElement.getAttribute("suffix-pattern"));
handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch);
preferPathMatcher |= useSuffixPatternMatch;
}
if (pathMatchingElement.hasAttribute("registered-suffixes-only")) {
Boolean useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("registered-suffixes-only"));
boolean useRegisteredSuffixPatternMatch = Boolean.parseBoolean(pathMatchingElement.getAttribute("registered-suffixes-only"));
handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch);
preferPathMatcher |= useRegisteredSuffixPatternMatch;
}

RuntimeBeanReference pathHelperRef = null;
if (pathMatchingElement.hasAttribute("path-helper")) {
pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper"));
preferPathMatcher = true;
}
pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(pathHelperRef, context, source);
handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef);

RuntimeBeanReference pathMatcherRef = null;
if (pathMatchingElement.hasAttribute("path-matcher")) {
pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher"));
preferPathMatcher = true;
}
pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(pathMatcherRef, context, source);
handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef);

if (preferPathMatcher) {
handlerMappingDef.getPropertyValues().add("patternParser", null);
}
}

}

private Properties getDefaultMediaTypes() {
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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.
Expand All @@ -23,7 +23,6 @@
import org.springframework.lang.Nullable;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.PathPattern;
Expand All @@ -34,14 +33,19 @@
* <ul>
* <li>{@link WebMvcConfigurationSupport#requestMappingHandlerMapping}</li>
* <li>{@link WebMvcConfigurationSupport#viewControllerHandlerMapping}</li>
* <li>{@link WebMvcConfigurationSupport#beanNameHandlerMapping}</li>
* <li>{@link WebMvcConfigurationSupport#routerFunctionMapping}</li>
* <li>{@link WebMvcConfigurationSupport#resourceHandlerMapping}</li>
* </ul>
*
* @author Brian Clozel
* @author Rossen Stoyanchev
* @since 4.0.3
*/
public class PathMatchConfigurer {

private boolean preferPathMatcher = false;

@Nullable
private PathPatternParser patternParser;

Expand Down Expand Up @@ -74,17 +78,28 @@ public class PathMatchConfigurer {


/**
* Enable use of parsed {@link PathPattern}s as described in
* {@link AbstractHandlerMapping#setPatternParser(PathPatternParser)}.
* <p><strong>Note:</strong> This is mutually exclusive with use of
* {@link #setUrlPathHelper(UrlPathHelper)} and
* {@link #setPathMatcher(PathMatcher)}.
* <p>By default this is not enabled.
* Set the {@link PathPatternParser} to parse {@link PathPattern patterns}
* with for URL path matching. Parsed patterns provide a more modern and
* efficient alternative to String path matching via {@link AntPathMatcher}.
* <p><strong>Note:</strong> This property is mutually exclusive with the
* following other, {@code AntPathMatcher} related properties:
* <ul>
* <li>{@link #setUseSuffixPatternMatch(Boolean)}
* <li>{@link #setUseRegisteredSuffixPatternMatch(Boolean)}
* <li>{@link #setUrlPathHelper(UrlPathHelper)}
* <li>{@link #setPathMatcher(PathMatcher)}
* </ul>
* <p>By default, as of 6.0, a {@link PathPatternParser} with default
* settings is used, which enables parsed {@link PathPattern patterns}.
* Set this property to {@code null} to fall back on String path matching via
* {@link AntPathMatcher} instead, or alternatively, setting one of the above
* listed {@code AntPathMatcher} related properties has the same effect.
* @param patternParser the parser to pre-parse patterns with
* @since 5.3
*/
public PathMatchConfigurer setPatternParser(PathPatternParser patternParser) {
public PathMatchConfigurer setPatternParser(@Nullable PathPatternParser patternParser) {
this.patternParser = patternParser;
this.preferPathMatcher = (patternParser == null);
return this;
}

Expand Down Expand Up @@ -120,9 +135,11 @@ public PathMatchConfigurer addPathPrefix(String prefix, Predicate<Class<?>> pred
/**
* Whether to use suffix pattern match (".*") when matching patterns to
* requests. If enabled a method mapped to "/users" also matches to "/users.*".
* <p><strong>Note:</strong> This property is mutually exclusive with
* {@link #setPatternParser(PathPatternParser)}. If set, it enables use of
* String path matching, unless a {@code PathPatternParser} is also
* explicitly set in which case this property is ignored.
* <p>By default this is set to {@code false}.
* <p><strong>Note:</strong> This property is mutually exclusive with and
* ignored when {@link #setPatternParser(PathPatternParser)} is set.
* @deprecated as of 5.2.4. See class-level note in
* {@link RequestMappingHandlerMapping} on the deprecation of path extension
* config options. As there is no replacement for this method, in 5.2.x it is
Expand All @@ -132,6 +149,7 @@ public PathMatchConfigurer addPathPrefix(String prefix, Predicate<Class<?>> pred
@Deprecated
public PathMatchConfigurer setUseSuffixPatternMatch(Boolean suffixPatternMatch) {
this.suffixPatternMatch = suffixPatternMatch;
this.preferPathMatcher |= suffixPatternMatch;
return this;
}

Expand All @@ -141,41 +159,66 @@ public PathMatchConfigurer setUseSuffixPatternMatch(Boolean suffixPatternMatch)
* {@link WebMvcConfigurer#configureContentNegotiation configure content
* negotiation}. This is generally recommended to reduce ambiguity and to
* avoid issues such as when a "." appears in the path for other reasons.
* <p><strong>Note:</strong> This property is mutually exclusive with
* {@link #setPatternParser(PathPatternParser)}. If set, it enables use of
* String path matching, unless a {@code PathPatternParser} is also
* explicitly set in which case this property is ignored.
* <p>By default this is set to "false".
* <p><strong>Note:</strong> This property is mutually exclusive with and
* ignored when {@link #setPatternParser(PathPatternParser)} is set.
* @deprecated as of 5.2.4. See class-level note in
* {@link RequestMappingHandlerMapping} on the deprecation of path extension
* config options.
*/
@Deprecated
public PathMatchConfigurer setUseRegisteredSuffixPatternMatch(Boolean registeredSuffixPatternMatch) {
this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
this.preferPathMatcher |= registeredSuffixPatternMatch;
return this;
}

/**
* Set the UrlPathHelper to use to resolve the mapping path for the application.
* <p><strong>Note:</strong> This property is mutually exclusive with and
* ignored when {@link #setPatternParser(PathPatternParser)} is set.
* <p><strong>Note:</strong> This property is mutually exclusive with
* {@link #setPatternParser(PathPatternParser)}. If set, it enables use of
* String path matching, unless a {@code PathPatternParser} is also
* explicitly set in which case this property is ignored.
* <p>By default this is an instance of {@link UrlPathHelper} with default
* settings.
*/
public PathMatchConfigurer setUrlPathHelper(UrlPathHelper urlPathHelper) {
this.urlPathHelper = urlPathHelper;
this.preferPathMatcher = true;
return this;
}

/**
* Set the PathMatcher to use for String pattern matching.
* <p>By default this is {@link AntPathMatcher}.
* <p><strong>Note:</strong> This property is mutually exclusive with and
* ignored when {@link #setPatternParser(PathPatternParser)} is set.
* <p><strong>Note:</strong> This property is mutually exclusive with
* {@link #setPatternParser(PathPatternParser)}. If set, it enables use of
* String path matching, unless a {@code PathPatternParser} is also
* explicitly set in which case this property is ignored.
* <p>By default this is an instance of {@link AntPathMatcher} with default
* settings.
*/
public PathMatchConfigurer setPathMatcher(PathMatcher pathMatcher) {
this.pathMatcher = pathMatcher;
this.preferPathMatcher = true;
return this;
}


/**
* Whether to prefer {@link PathMatcher}. This is the case when either is true:
* <ul>
* <li>{@link PathPatternParser} is explicitly set to {@code null}.
* <li>{@link PathPatternParser} is not explicitly set, and a
* {@link PathMatcher} related option is explicitly set.
* </ul>
* @since 6.0
*/
protected boolean preferPathMatcher() {
return (this.patternParser == null && this.preferPathMatcher);
}

/**
* Return the {@link PathPatternParser} to use, if configured.
* @since 5.3
Expand Down

0 comments on commit 92cf1e1

Please sign in to comment.