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:
Rossen Stoyanchev
2013-02-15 11:31:32 -05:00
parent eda53ec1d8
commit 5b1165b102
8 changed files with 42 additions and 22 deletions

View File

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

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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

View File

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