Browse Source

SPR-6280 - PathVariable resolution does not work properly

pull/23217/head
Arjen Poutsma 15 years ago
parent
commit
ef50082cad
  1. 2
      org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java
  2. 2
      org.springframework.core/src/main/java/org/springframework/util/AntPathStringMatcher.java
  3. 13
      org.springframework.core/src/test/java/org/springframework/util/AntPathMatcherTests.java
  4. 17
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java
  5. 71
      org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java
  6. 5
      org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java

2
org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java

@ -412,7 +412,7 @@ public class AntPathMatcher implements PathMatcher { @@ -412,7 +412,7 @@ public class AntPathMatcher implements PathMatcher {
else if (bracketCount2 < bracketCount1) {
return 1;
}
return 0;
return pattern2.length() - pattern1.length();
}
}

2
org.springframework.core/src/main/java/org/springframework/util/AntPathStringMatcher.java

@ -36,7 +36,7 @@ class AntPathStringMatcher { @@ -36,7 +36,7 @@ class AntPathStringMatcher {
private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{([^/]+?)\\}");
private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";
private static final String DEFAULT_VARIABLE_PATTERN = "([^\\.]*)";
private final Pattern pattern;

13
org.springframework.core/src/test/java/org/springframework/util/AntPathMatcherTests.java

@ -395,6 +395,11 @@ public class AntPathMatcherTests { @@ -395,6 +395,11 @@ public class AntPathMatcherTests {
assertEquals(-1, comparator.compare("/hotels/*", "/hotels/*/**"));
assertEquals(1, comparator.compare("/hotels/*/**", "/hotels/*"));
assertEquals(-1, comparator.compare("/hotels/new", "/hotels/new.*"));
// longer is better
assertEquals(1, comparator.compare("/hotels", "/hotels2"));
}
@Test
@ -467,6 +472,14 @@ public class AntPathMatcherTests { @@ -467,6 +472,14 @@ public class AntPathMatcherTests {
assertEquals("/hotels/{hotel}", paths.get(1));
assertEquals("/hotels/*", paths.get(2));
paths.clear();
paths.add("/hotels/ne*");
paths.add("/hotels/n*");
Collections.shuffle(paths);
Collections.sort(paths, comparator);
assertEquals("/hotels/ne*", paths.get(0));
assertEquals("/hotels/n*", paths.get(1));
paths.clear();
}
}

17
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java

@ -19,6 +19,8 @@ package org.springframework.web.servlet.handler; @@ -19,6 +19,8 @@ package org.springframework.web.servlet.handler;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -214,12 +216,19 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { @@ -214,12 +216,19 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
return buildPathExposingHandler(handler, urlPath, null);
}
// Pattern match?
String bestPathMatch = null;
List<String> matchingPaths = new ArrayList<String>();
for (String registeredPath : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPath, urlPath) &&
(bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {
bestPathMatch = registeredPath;
if (getPathMatcher().match(registeredPath, urlPath)) {
matchingPaths.add(registeredPath);
}
}
String bestPathMatch = null;
if (!matchingPaths.isEmpty()) {
Collections.sort(matchingPaths, getPathMatcher().getPatternComparator(urlPath));
if (logger.isDebugEnabled()) {
logger.debug("Matching path for request [" + urlPath + "] are " + matchingPaths);
}
bestPathMatch = matchingPaths.get(0);
}
if (bestPathMatch != null) {
handler = this.handlerMap.get(bestPathMatch);

71
org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java

@ -221,6 +221,22 @@ public class UriTemplateServletAnnotationControllerTests { @@ -221,6 +221,22 @@ public class UriTemplateServletAnnotationControllerTests {
}
@Test
public void multiPaths() throws Exception {
initServlet(MultiPathController.class);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/category/page/5");
MockHttpServletResponse response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("handle4-page-5", response.getContentAsString());
request = new MockHttpServletRequest("GET", "/category/page/5.html");
response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("handle4-page-5", response.getContentAsString());
}
private void initServlet(final Class<?> controllerclass) throws ServletException {
servlet = new DispatcherServlet() {
@Override
@ -258,6 +274,17 @@ public class UriTemplateServletAnnotationControllerTests { @@ -258,6 +274,17 @@ public class UriTemplateServletAnnotationControllerTests {
assertEquals("test-hotel.with.dot", response.getContentAsString());
}
@Test
public void customRegex() throws Exception {
initServlet(CustomRegexController.class);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42");
MockHttpServletResponse response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("test-42", response.getContentAsString());
}
/*
* Controllers
*/
@ -371,12 +398,24 @@ public class UriTemplateServletAnnotationControllerTests { @@ -371,12 +398,24 @@ public class UriTemplateServletAnnotationControllerTests {
@RequestMapping("hotels")
public static class ImplicitSubPathController {
@RequestMapping("{hotel}")
@RequestMapping("{hotel:.*}")
public void handleHotel(@PathVariable String hotel, Writer writer) throws IOException {
writer.write("test-" + hotel);
}
}
@Controller
public static class CustomRegexController {
@RequestMapping("/{root:\\d+}")
public void handle(@PathVariable("root") int root, Writer writer) throws IOException {
assertEquals("Invalid path variable value", 42, root);
writer.write("test-" + root);
}
}
@Controller
@RequestMapping("hotels")
public static class CrudController {
@ -429,4 +468,34 @@ public class UriTemplateServletAnnotationControllerTests { @@ -429,4 +468,34 @@ public class UriTemplateServletAnnotationControllerTests {
}
}
@Controller
@RequestMapping("/category")
public static class MultiPathController {
@RequestMapping(value = {"/{category}/page/{page}", "/**/{category}/page/{page}"})
public void category(@PathVariable String category, @PathVariable int page, Writer writer) throws IOException {
writer.write("handle1-");
writer.write("category-" + category);
writer.write("page-" + page);
}
@RequestMapping(value = {"/{category}", "/**/{category}"})
public void category(@PathVariable String category, Writer writer) throws IOException {
writer.write("handle2-");
writer.write("category-" + category);
}
@RequestMapping(value = {""})
public void category(Writer writer) throws IOException {
writer.write("handle3");
}
@RequestMapping(value = {"/page/{page}"})
public void category(@PathVariable int page, Writer writer) throws IOException {
writer.write("handle4-");
writer.write("page-" + page);
}
}
}

5
org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java

@ -73,7 +73,10 @@ import java.lang.annotation.Target; @@ -73,7 +73,10 @@ import java.lang.annotation.Target;
* exposed by the Servlet/Portlet API.
* <li>{@link PathVariable @PathVariable} annotated parameters for access to
* URI template values (i.e. /hotels/{hotel}). Variable values will be
* converted to the declared method argument type.
* converted to the declared method argument type. By default, the URI template
* will match against the regular expression {@code [^\.]*} (i.e. any character
* other than period), but this can be changed by specifying another regular
* expression, like so: /hotels/{hotel:\d+}.
* <li>{@link RequestParam @RequestParam} annotated parameters for access to
* specific Servlet/Portlet request parameters. Parameter values will be
* converted to the declared method argument type. Additionally,

Loading…
Cancel
Save