Skip to content

Commit 5b1165b

Browse files
committed
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
1 parent eda53ec commit 5b1165b

File tree

8 files changed

+42
-22
lines changed

8 files changed

+42
-22
lines changed

spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,8 @@ protected String determineEncoding(HttpServletRequest request) {
441441
* @return the updated URI string
442442
*/
443443
public String removeSemicolonContent(String requestUri) {
444-
if (this.removeSemicolonContent) {
445-
return removeSemicolonContentInternal(requestUri);
446-
}
447-
return removeJsessionid(requestUri);
444+
return this.removeSemicolonContent ?
445+
removeSemicolonContentInternal(requestUri) : removeJsessionid(requestUri);
448446
}
449447

450448
private String removeSemicolonContentInternal(String requestUri) {

spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
154154
handlerMappingDef.setSource(source);
155155
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
156156
handlerMappingDef.getPropertyValues().add("order", 0);
157-
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", false);
158157
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
159158
String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);
160159

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
192192
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
193193
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
194194
handlerMapping.setOrder(0);
195-
handlerMapping.setRemoveSemicolonContent(false);
196195
handlerMapping.setInterceptors(getInterceptors());
197196
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
198197
return handlerMapping;

spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ public void setUrlDecode(boolean urlDecode) {
127127

128128
/**
129129
* Set if ";" (semicolon) content should be stripped from the request URI.
130+
* <p>The default value is {@code false}.
130131
* @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean)
131132
*/
132133
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {

spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.web.method.HandlerMethod;
3838
import org.springframework.web.method.HandlerMethodSelector;
3939
import org.springframework.web.servlet.HandlerMapping;
40+
import org.springframework.web.util.UrlPathHelper;
4041

4142
/**
4243
* Abstract base class for {@link HandlerMapping} implementations that define a
@@ -61,6 +62,12 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
6162
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
6263

6364

65+
public AbstractHandlerMethodMapping() {
66+
UrlPathHelper pathHelper = new UrlPathHelper();
67+
pathHelper.setRemoveSemicolonContent(false);
68+
setUrlPathHelper(pathHelper);
69+
}
70+
6471
/**
6572
* Whether to detect handler methods in beans in ancestor ApplicationContexts.
6673
* <p>Default is "false": Only beans in the current ApplicationContext are

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,16 @@
4242
*/
4343
public final class PatternsRequestCondition extends AbstractRequestCondition<PatternsRequestCondition> {
4444

45+
private static UrlPathHelper pathHelperNoSemicolonContent;
46+
47+
static {
48+
pathHelperNoSemicolonContent = new UrlPathHelper();
49+
pathHelperNoSemicolonContent.setRemoveSemicolonContent(true);
50+
}
51+
4552
private final Set<String> patterns;
4653

47-
private final UrlPathHelper urlPathHelper;
54+
private final UrlPathHelper pathHelper;
4855

4956
private final PathMatcher pathMatcher;
5057

@@ -105,7 +112,7 @@ private PatternsRequestCondition(Collection<String> patterns, UrlPathHelper urlP
105112
List<String> fileExtensions) {
106113

107114
this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
108-
this.urlPathHelper = urlPathHelper != null ? urlPathHelper : new UrlPathHelper();
115+
this.pathHelper = urlPathHelper != null ? urlPathHelper : new UrlPathHelper();
109116
this.pathMatcher = pathMatcher != null ? pathMatcher : new AntPathMatcher();
110117
this.useSuffixPatternMatch = useSuffixPatternMatch;
111118
this.useTrailingSlashMatch = useTrailingSlashMatch;
@@ -179,7 +186,7 @@ else if (!other.patterns.isEmpty()) {
179186
else {
180187
result.add("");
181188
}
182-
return new PatternsRequestCondition(result, this.urlPathHelper, this.pathMatcher, this.useSuffixPatternMatch,
189+
return new PatternsRequestCondition(result, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
183190
this.useTrailingSlashMatch, this.fileExtensions);
184191
}
185192

@@ -206,17 +213,24 @@ public PatternsRequestCondition getMatchingCondition(HttpServletRequest request)
206213
if (this.patterns.isEmpty()) {
207214
return this;
208215
}
209-
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
216+
217+
String lookupPath = this.pathHelper.getLookupPathForRequest(request);
218+
String lookupPathNoSemicolonContent = (lookupPath.indexOf(';') != -1) ?
219+
pathHelperNoSemicolonContent.getLookupPathForRequest(request) : null;
220+
210221
List<String> matches = new ArrayList<String>();
211222
for (String pattern : patterns) {
212223
String match = getMatchingPattern(pattern, lookupPath);
224+
if (match == null && lookupPathNoSemicolonContent != null) {
225+
match = getMatchingPattern(pattern, lookupPathNoSemicolonContent);
226+
}
213227
if (match != null) {
214228
matches.add(match);
215229
}
216230
}
217231
Collections.sort(matches, this.pathMatcher.getPatternComparator(lookupPath));
218232
return matches.isEmpty() ? null :
219-
new PatternsRequestCondition(matches, this.urlPathHelper, this.pathMatcher, this.useSuffixPatternMatch,
233+
new PatternsRequestCondition(matches, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
220234
this.useTrailingSlashMatch, this.fileExtensions);
221235
}
222236

@@ -225,7 +239,7 @@ private String getMatchingPattern(String pattern, String lookupPath) {
225239
return pattern;
226240
}
227241
if (this.useSuffixPatternMatch) {
228-
if (useSmartSuffixPatternMatch(pattern, lookupPath)) {
242+
if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
229243
for (String extension : this.fileExtensions) {
230244
if (this.pathMatcher.match(pattern + extension, lookupPath)) {
231245
return pattern + extension;
@@ -251,14 +265,6 @@ private String getMatchingPattern(String pattern, String lookupPath) {
251265
return null;
252266
}
253267

254-
/**
255-
* Whether to match by known file extensions. Return "true" if file extensions
256-
* are configured, and the lookup path has a suffix.
257-
*/
258-
private boolean useSmartSuffixPatternMatch(String pattern, String lookupPath) {
259-
return (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) ;
260-
}
261-
262268
/**
263269
* Compare the two conditions based on the URL patterns they contain.
264270
* Patterns are compared one at a time, from top to bottom via
@@ -272,7 +278,7 @@ private boolean useSmartSuffixPatternMatch(String pattern, String lookupPath) {
272278
* the best matches on top.
273279
*/
274280
public int compareTo(PatternsRequestCondition other, HttpServletRequest request) {
275-
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
281+
String lookupPath = this.pathHelper.getLookupPathForRequest(request);
276282
Comparator<String> patternComparator = this.pathMatcher.getPatternComparator(lookupPath);
277283

278284
Iterator<String> iterator = patterns.iterator();

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/condition/PatternsRequestConditionTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import org.junit.Test;
2929
import org.springframework.mock.web.test.MockHttpServletRequest;
30+
import org.springframework.web.util.UrlPathHelper;
3031

3132
/**
3233
* @author Rossen Stoyanchev
@@ -185,6 +186,16 @@ public void matchPatternContainsExtension() {
185186
assertNull(match);
186187
}
187188

189+
@Test
190+
public void matchIgnorePathParams() {
191+
UrlPathHelper pathHelper = new UrlPathHelper();
192+
pathHelper.setRemoveSemicolonContent(false);
193+
PatternsRequestCondition condition = new PatternsRequestCondition(new String[] {"/foo/bar"}, pathHelper, null, true, true);
194+
PatternsRequestCondition match = condition.getMatchingCondition(new MockHttpServletRequest("GET", "/foo;q=1/bar;s=1"));
195+
196+
assertNotNull(match);
197+
}
198+
188199
@Test
189200
public void compareEqualPatterns() {
190201
PatternsRequestCondition c1 = new PatternsRequestCondition("/foo*");

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ public void setUp() throws Exception {
9090

9191
this.handlerMapping = new TestRequestMappingInfoHandlerMapping();
9292
this.handlerMapping.registerHandler(testController);
93-
this.handlerMapping.setRemoveSemicolonContent(false);
9493
}
9594

9695
@Test

0 commit comments

Comments
 (0)