Ignore path parameters in request mappings
Before this change the presence of path params (e.g. "/foo;q=1/bar")
expected the request mapping to contain a URI variable in the place of
semicolon content (e.g. either "/{foo}/bar" or "/{foo};{fooParams}").
The change ensures path params are ignored in @RequestMapping patterns
so that "/foo/bar" matches to "/foo;q=1/bar" as well as
"/foo;q=1;p=2/bar".
Along with this change, the RequestMappingHandlerMapping no longer
defaults to having semicolon content removed from the URL, which means
@MatrixVariable is supported by default without the need for any
further configuration.
Issue: SPR-10234
This commit is contained in:
@@ -154,7 +154,6 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
handlerMappingDef.setSource(source);
|
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
handlerMappingDef.getPropertyValues().add("order", 0);
|
||||
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", false);
|
||||
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
|
||||
String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);
|
||||
|
||||
|
||||
@@ -192,7 +192,6 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
|
||||
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
|
||||
handlerMapping.setOrder(0);
|
||||
handlerMapping.setRemoveSemicolonContent(false);
|
||||
handlerMapping.setInterceptors(getInterceptors());
|
||||
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
|
||||
return handlerMapping;
|
||||
|
||||
@@ -127,6 +127,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
|
||||
/**
|
||||
* Set if ";" (semicolon) content should be stripped from the request URI.
|
||||
* <p>The default value is {@code false}.
|
||||
* @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean)
|
||||
*/
|
||||
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link HandlerMapping} implementations that define a
|
||||
@@ -61,6 +62,12 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
|
||||
|
||||
|
||||
public AbstractHandlerMethodMapping() {
|
||||
UrlPathHelper pathHelper = new UrlPathHelper();
|
||||
pathHelper.setRemoveSemicolonContent(false);
|
||||
setUrlPathHelper(pathHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to detect handler methods in beans in ancestor ApplicationContexts.
|
||||
* <p>Default is "false": Only beans in the current ApplicationContext are
|
||||
|
||||
@@ -42,9 +42,16 @@ import org.springframework.web.util.UrlPathHelper;
|
||||
*/
|
||||
public final class PatternsRequestCondition extends AbstractRequestCondition<PatternsRequestCondition> {
|
||||
|
||||
private static UrlPathHelper pathHelperNoSemicolonContent;
|
||||
|
||||
static {
|
||||
pathHelperNoSemicolonContent = new UrlPathHelper();
|
||||
pathHelperNoSemicolonContent.setRemoveSemicolonContent(true);
|
||||
}
|
||||
|
||||
private final Set<String> patterns;
|
||||
|
||||
private final UrlPathHelper urlPathHelper;
|
||||
private final UrlPathHelper pathHelper;
|
||||
|
||||
private final PathMatcher pathMatcher;
|
||||
|
||||
@@ -105,7 +112,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
List<String> fileExtensions) {
|
||||
|
||||
this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
|
||||
this.urlPathHelper = urlPathHelper != null ? urlPathHelper : new UrlPathHelper();
|
||||
this.pathHelper = urlPathHelper != null ? urlPathHelper : new UrlPathHelper();
|
||||
this.pathMatcher = pathMatcher != null ? pathMatcher : new AntPathMatcher();
|
||||
this.useSuffixPatternMatch = useSuffixPatternMatch;
|
||||
this.useTrailingSlashMatch = useTrailingSlashMatch;
|
||||
@@ -179,7 +186,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
else {
|
||||
result.add("");
|
||||
}
|
||||
return new PatternsRequestCondition(result, this.urlPathHelper, this.pathMatcher, this.useSuffixPatternMatch,
|
||||
return new PatternsRequestCondition(result, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
|
||||
this.useTrailingSlashMatch, this.fileExtensions);
|
||||
}
|
||||
|
||||
@@ -206,17 +213,24 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
if (this.patterns.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
|
||||
|
||||
String lookupPath = this.pathHelper.getLookupPathForRequest(request);
|
||||
String lookupPathNoSemicolonContent = (lookupPath.indexOf(';') != -1) ?
|
||||
pathHelperNoSemicolonContent.getLookupPathForRequest(request) : null;
|
||||
|
||||
List<String> matches = new ArrayList<String>();
|
||||
for (String pattern : patterns) {
|
||||
String match = getMatchingPattern(pattern, lookupPath);
|
||||
if (match == null && lookupPathNoSemicolonContent != null) {
|
||||
match = getMatchingPattern(pattern, lookupPathNoSemicolonContent);
|
||||
}
|
||||
if (match != null) {
|
||||
matches.add(match);
|
||||
}
|
||||
}
|
||||
Collections.sort(matches, this.pathMatcher.getPatternComparator(lookupPath));
|
||||
return matches.isEmpty() ? null :
|
||||
new PatternsRequestCondition(matches, this.urlPathHelper, this.pathMatcher, this.useSuffixPatternMatch,
|
||||
new PatternsRequestCondition(matches, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
|
||||
this.useTrailingSlashMatch, this.fileExtensions);
|
||||
}
|
||||
|
||||
@@ -225,7 +239,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
return pattern;
|
||||
}
|
||||
if (this.useSuffixPatternMatch) {
|
||||
if (useSmartSuffixPatternMatch(pattern, lookupPath)) {
|
||||
if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
|
||||
for (String extension : this.fileExtensions) {
|
||||
if (this.pathMatcher.match(pattern + extension, lookupPath)) {
|
||||
return pattern + extension;
|
||||
@@ -251,14 +265,6 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to match by known file extensions. Return "true" if file extensions
|
||||
* are configured, and the lookup path has a suffix.
|
||||
*/
|
||||
private boolean useSmartSuffixPatternMatch(String pattern, String lookupPath) {
|
||||
return (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the two conditions based on the URL patterns they contain.
|
||||
* Patterns are compared one at a time, from top to bottom via
|
||||
@@ -272,7 +278,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
* the best matches on top.
|
||||
*/
|
||||
public int compareTo(PatternsRequestCondition other, HttpServletRequest request) {
|
||||
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
|
||||
String lookupPath = this.pathHelper.getLookupPathForRequest(request);
|
||||
Comparator<String> patternComparator = this.pathMatcher.getPatternComparator(lookupPath);
|
||||
|
||||
Iterator<String> iterator = patterns.iterator();
|
||||
|
||||
Reference in New Issue
Block a user