Fix issue with extracting matrix variables
The servlet spec recommends removing path parameters from the contextPath, servletPath, and pathInfo but not from the requestURI. This poses a challenge for the UrlPathHelper, which determines the lookup path by comparing the above. This change introduces a method that matches the requestURI to the contextPath and servletPath ignoring path parameters (i.e. matrix variables) for comparison purposes while also preserving them in the resulting lookup path.
This commit is contained in:
@@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -172,9 +173,10 @@ public class UrlPathHelper {
|
||||
public String getPathWithinServletMapping(HttpServletRequest request) {
|
||||
String pathWithinApp = getPathWithinApplication(request);
|
||||
String servletPath = getServletPath(request);
|
||||
if (pathWithinApp.startsWith(servletPath)) {
|
||||
String path = getRemainingPath(pathWithinApp, servletPath, false);
|
||||
if (path != null) {
|
||||
// Normal case: URI contains servlet path.
|
||||
return pathWithinApp.substring(servletPath.length());
|
||||
return path;
|
||||
}
|
||||
else {
|
||||
// Special case: URI is different from servlet path.
|
||||
@@ -195,17 +197,54 @@ public class UrlPathHelper {
|
||||
public String getPathWithinApplication(HttpServletRequest request) {
|
||||
String contextPath = getContextPath(request);
|
||||
String requestUri = getRequestUri(request);
|
||||
if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) {
|
||||
String path = getRemainingPath(requestUri, contextPath, true);
|
||||
if (path != null) {
|
||||
// Normal case: URI contains context path.
|
||||
String path = requestUri.substring(contextPath.length());
|
||||
return (StringUtils.hasText(path) ? path : "/");
|
||||
}
|
||||
else {
|
||||
// Special case: rather unusual.
|
||||
return requestUri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the given "mapping" to the start of the "requestUri" and if there
|
||||
* is a match return the extra part. This method is needed because the
|
||||
* context path and the servlet path returned by the HttpServletRequest are
|
||||
* stripped of semicolon content unlike the requesUri.
|
||||
*/
|
||||
private String getRemainingPath(String requestUri, String mapping, boolean ignoreCase) {
|
||||
int index1 = 0;
|
||||
int index2 = 0;
|
||||
for ( ; (index1 < requestUri.length()) && (index2 < mapping.length()); index1++, index2++) {
|
||||
char c1 = requestUri.charAt(index1);
|
||||
char c2 = mapping.charAt(index2);
|
||||
if (c1 == ';') {
|
||||
index1 = requestUri.indexOf('/', index1);
|
||||
if (index1 == -1) {
|
||||
return null;
|
||||
}
|
||||
c1 = requestUri.charAt(index1);
|
||||
}
|
||||
if (c1 == c2) {
|
||||
continue;
|
||||
}
|
||||
if (ignoreCase && (Character.toLowerCase(c1) == Character.toLowerCase(c2))) {
|
||||
continue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (index2 != mapping.length()) {
|
||||
return null;
|
||||
}
|
||||
if (index1 == requestUri.length()) {
|
||||
return "";
|
||||
}
|
||||
else if (requestUri.charAt(index1) == ';') {
|
||||
index1 = requestUri.indexOf('/', index1);
|
||||
}
|
||||
return (index1 != -1) ? requestUri.substring(index1) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the request URI for the given request, detecting an include request
|
||||
|
||||
@@ -69,6 +69,15 @@ public class UrlPathHelperTests {
|
||||
assertEquals("Incorrect path returned", "/welcome.html", helper.getPathWithinApplication(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPathWithinServlet() {
|
||||
request.setContextPath("/petclinic");
|
||||
request.setServletPath("/main");
|
||||
request.setRequestURI("/petclinic/main/welcome.html");
|
||||
|
||||
assertEquals("Incorrect path returned", "/welcome.html", helper.getPathWithinServletMapping(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestUri() {
|
||||
request.setRequestURI("/welcome.html");
|
||||
@@ -104,6 +113,29 @@ public class UrlPathHelperTests {
|
||||
assertEquals("jsessionid should always be removed", "/foo;a=b;c=d", helper.getRequestUri(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLookupPathWithSemicolonContent() {
|
||||
helper.setRemoveSemicolonContent(false);
|
||||
|
||||
request.setContextPath("/petclinic");
|
||||
request.setServletPath("/main");
|
||||
request.setRequestURI("/petclinic;a=b/main;b=c/welcome.html;c=d");
|
||||
|
||||
assertEquals("/welcome.html;c=d", helper.getLookupPathForRequest(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLookupPathWithSemicolonContentAndNullPathInfo() {
|
||||
helper.setRemoveSemicolonContent(false);
|
||||
|
||||
request.setContextPath("/petclinic");
|
||||
request.setServletPath("/welcome.html");
|
||||
request.setRequestURI("/petclinic;a=b/welcome.html;c=d");
|
||||
|
||||
assertEquals("/welcome.html;c=d", helper.getLookupPathForRequest(request));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// suite of tests root requests for default servlets (SRV 11.2) on Websphere vs Tomcat and other containers
|
||||
// see: http://jira.springframework.org/browse/SPR-7064
|
||||
|
||||
@@ -116,7 +116,7 @@ public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMeth
|
||||
protected void handleMissingValue(String name, MethodParameter param) throws ServletRequestBindingException {
|
||||
String paramType = param.getParameterType().getName();
|
||||
throw new ServletRequestBindingException(
|
||||
"Missing URI path parameter '" + name + "' for method parameter type [" + paramType + "]");
|
||||
"Missing matrix variable '" + name + "' for method parameter type [" + paramType + "]");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user