Decode path vars when url decoding is turned off
When URL decoding is turned off in AbstractHandlerMapping, the extracted path variables are also not encoded. Turning off URL decoding may be necessary for request mapping to work correctly when the path may contain the (encoded) special character '/'. At the same time there is no good reason not to leave path variables encoded. This change ensures path variables are encoded when URL decoding is turned off. Issue: SPR-9098
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2010 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -18,12 +18,15 @@ package org.springframework.web.util;
|
|||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,6 +40,7 @@ import org.springframework.util.StringUtils;
|
|||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Rob Harrop
|
* @author Rob Harrop
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
* @since 14.01.2004
|
* @since 14.01.2004
|
||||||
*/
|
*/
|
||||||
public class UrlPathHelper {
|
public class UrlPathHelper {
|
||||||
@@ -333,21 +337,25 @@ public class UrlPathHelper {
|
|||||||
*/
|
*/
|
||||||
public String decodeRequestString(HttpServletRequest request, String source) {
|
public String decodeRequestString(HttpServletRequest request, String source) {
|
||||||
if (this.urlDecode) {
|
if (this.urlDecode) {
|
||||||
String enc = determineEncoding(request);
|
return decodeInternal(request, source);
|
||||||
try {
|
|
||||||
return UriUtils.decode(source, enc);
|
|
||||||
}
|
|
||||||
catch (UnsupportedEncodingException ex) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn("Could not decode request string [" + source + "] with encoding '" + enc +
|
|
||||||
"': falling back to platform default encoding; exception message: " + ex.getMessage());
|
|
||||||
}
|
|
||||||
return URLDecoder.decode(source);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String decodeInternal(HttpServletRequest request, String source) {
|
||||||
|
String enc = determineEncoding(request);
|
||||||
|
try {
|
||||||
|
return UriUtils.decode(source, enc);
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException ex) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
logger.warn("Could not decode request string [" + source + "] with encoding '" + enc +
|
||||||
|
"': falling back to platform default encoding; exception message: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
return URLDecoder.decode(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the encoding for the given request.
|
* Determine the encoding for the given request.
|
||||||
* Can be overridden in subclasses.
|
* Can be overridden in subclasses.
|
||||||
@@ -366,6 +374,27 @@ public class UrlPathHelper {
|
|||||||
return enc;
|
return enc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the given URI path variables via {@link #decodeRequestString(HttpServletRequest, String)}
|
||||||
|
* unless {@link #setUrlDecode(boolean)} is set to {@code true} in which case
|
||||||
|
* it is assumed the URL path from which the variables were extracted is
|
||||||
|
* already decoded through a call to {@link #getLookupPathForRequest(HttpServletRequest)}.
|
||||||
|
* @param request current HTTP request
|
||||||
|
* @param vars URI variables extracted from the URL path
|
||||||
|
* @return the same Map or a new Map instance
|
||||||
|
*/
|
||||||
|
public Map<String, String> decodePathVariables(HttpServletRequest request, Map<String, String> vars) {
|
||||||
|
if (this.urlDecode) {
|
||||||
|
return vars;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Map<String, String> decodedVars = new LinkedHashMap<String, String>(vars.size());
|
||||||
|
for (Entry<String, String> entry : vars.entrySet()) {
|
||||||
|
decodedVars.put(entry.getKey(), decodeInternal(request, entry.getValue()));
|
||||||
|
}
|
||||||
|
return decodedVars;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean shouldRemoveTrailingServletPathSlash(HttpServletRequest request) {
|
private boolean shouldRemoveTrailingServletPathSlash(HttpServletRequest request) {
|
||||||
if (request.getAttribute(WEBSPHERE_URI_ATTRIBUTE) == null) {
|
if (request.getAttribute(WEBSPHERE_URI_ATTRIBUTE) == null) {
|
||||||
|
|||||||
@@ -89,8 +89,9 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
|||||||
String bestPattern = patterns.isEmpty() ? lookupPath : patterns.iterator().next();
|
String bestPattern = patterns.isEmpty() ? lookupPath : patterns.iterator().next();
|
||||||
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
|
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
|
||||||
|
|
||||||
Map<String, String> uriTemplateVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
|
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
|
||||||
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
|
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
|
||||||
|
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedVars);
|
||||||
|
|
||||||
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
|
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
|
||||||
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
|
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -202,6 +202,30 @@ public class RequestMappingInfoHandlerMappingTests {
|
|||||||
assertEquals("2", uriVariables.get("path2"));
|
assertEquals("2", uriVariables.get("path2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SPR-9098
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void uriTemplateVariablesDecode() {
|
||||||
|
PatternsRequestCondition patterns = new PatternsRequestCondition("/{group}/{identifier}");
|
||||||
|
RequestMappingInfo key = new RequestMappingInfo(patterns, null, null, null, null, null, null);
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/group/a%2Fb");
|
||||||
|
|
||||||
|
UrlPathHelper pathHelper = new UrlPathHelper();
|
||||||
|
pathHelper.setUrlDecode(false);
|
||||||
|
String lookupPath = pathHelper.getLookupPathForRequest(request);
|
||||||
|
|
||||||
|
this.mapping.setUrlPathHelper(pathHelper);
|
||||||
|
this.mapping.handleMatch(key, lookupPath, request);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, String> uriVariables =
|
||||||
|
(Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||||
|
|
||||||
|
assertNotNull(uriVariables);
|
||||||
|
assertEquals("group", uriVariables.get("group"));
|
||||||
|
assertEquals("a/b", uriVariables.get("identifier"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bestMatchingPatternAttribute() {
|
public void bestMatchingPatternAttribute() {
|
||||||
PatternsRequestCondition patterns = new PatternsRequestCondition("/{path1}/2", "/**");
|
PatternsRequestCondition patterns = new PatternsRequestCondition("/{path1}/2", "/**");
|
||||||
|
|||||||
1
src/dist/changelog.txt
vendored
1
src/dist/changelog.txt
vendored
@@ -27,6 +27,7 @@ Changes in version 3.2 M1
|
|||||||
* add CompositeRequestCondition for use with multiple custom request mapping conditions
|
* add CompositeRequestCondition for use with multiple custom request mapping conditions
|
||||||
* add ability to configure custom MessageCodesResolver through the MVC Java config
|
* add ability to configure custom MessageCodesResolver through the MVC Java config
|
||||||
* add option in MappingJacksonJsonView for setting the Content-Length header
|
* add option in MappingJacksonJsonView for setting the Content-Length header
|
||||||
|
* decode path variables when url decoding is turned off in AbstractHandlerMapping
|
||||||
|
|
||||||
Changes in version 3.1.1 (2012-02-16)
|
Changes in version 3.1.1 (2012-02-16)
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user