diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractFlashMapManager.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractFlashMapManager.java index 503662876f63..c5382f1dfec7 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractFlashMapManager.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractFlashMapManager.java @@ -16,17 +16,19 @@ package org.springframework.web.servlet.support; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.MultiValueMap; @@ -35,6 +37,7 @@ import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.FlashMapManager; import org.springframework.web.util.UrlPathHelper; +import org.springframework.web.util.WebUtils; /** * A base class for {@link FlashMapManager} implementations. @@ -229,14 +232,44 @@ private String decodeAndNormalizePath(String path, HttpServletRequest request) { private void decodeParameters(MultiValueMap params, HttpServletRequest request) { for (String name : new ArrayList(params.keySet())) { + String decodedName = decodeParameter(request, name); for (String value : new ArrayList(params.remove(name))) { - name = getUrlPathHelper().decodeRequestString(request, name); - value = getUrlPathHelper().decodeRequestString(request, value); - params.add(name, value); + params.add(decodedName, decodeParameter(request, value)); } } } + /** + * decode request parameter(name or value). + * + * @param request the current request + * @param target target string + * @return decoded string + * @see java.net.URLDecoder#decode(String, String) + * @see java.net.URLDecoder#decode(String) + */ + @SuppressWarnings("deprecation") + protected String decodeParameter(HttpServletRequest request, String target) { + String enc = request.getCharacterEncoding(); + if (enc == null) { + enc = WebUtils.DEFAULT_CHARACTER_ENCODING; + } + + try { + return URLDecoder.decode(target, enc); + } + catch (UnsupportedEncodingException e) { + if (logger.isWarnEnabled()) { + logger.warn("Could not decode request string [" + + target + + "] with encoding '" + + enc + + "': falling back to platform default encoding; exception message: " + + e.getMessage()); + } + return URLDecoder.decode(target); + } + } /** * Retrieve saved FlashMap instances from the underlying storage. diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/support/FlashMapManagerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/support/FlashMapManagerTests.java index f0399ada7d1e..3716b569b8b7 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/support/FlashMapManagerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/support/FlashMapManagerTests.java @@ -280,6 +280,62 @@ public void saveOutputFlashMapDecodeParameters() throws Exception { assertEquals(Arrays.asList("value"), targetRequestParams.get(":/?#[]@")); } + // SPR-11821 + + @Test + public void saveOutputFlashMapDecodeParametersContainsWhiteSpace() { + this.request.setCharacterEncoding("UTF-8"); + + FlashMap flashMap = new FlashMap(); + flashMap.put("anyKey", "anyValue"); + + flashMap.addTargetRequestParam("key", "value"); // normal value + flashMap.addTargetRequestParam("ke+y1", "val+ue"); // contains white space(+) + flashMap.addTargetRequestParam("ke%20y2", "val%20ue"); // contains white space(%20) + flashMap.addTargetRequestParam("ke%2By3", "val%2bue"); // contains plus(%2b) + + this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); + + MultiValueMap targetRequestParams = flashMap.getTargetRequestParams(); + assertEquals(Arrays.asList("value"), targetRequestParams.get("key")); + assertEquals(Arrays.asList("val ue"), targetRequestParams.get("ke y1")); + assertEquals(Arrays.asList("val ue"), targetRequestParams.get("ke y2")); + assertEquals(Arrays.asList("val+ue"), targetRequestParams.get("ke+y3")); + } + + @Test + public void saveOutputFlashMapDecodeParametersCharacterEncodingIsNull() { + this.request.setCharacterEncoding(null); + + FlashMap flashMap = new FlashMap(); + flashMap.put("anyKey", "anyValue"); + + flashMap.addTargetRequestParam("ke+y1", "val+ue"); // contains white space(+) + flashMap.addTargetRequestParam("ke%20y2", "val%20ue"); // contains white space(%20) + + this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); + + MultiValueMap targetRequestParams = flashMap.getTargetRequestParams(); + assertEquals(Arrays.asList("val ue"), targetRequestParams.get("ke y1")); + assertEquals(Arrays.asList("val ue"), targetRequestParams.get("ke y2")); + } + + @Test + public void saveOutputFlashMapDecodeParametersUnsupportedEncoding() { + this.request.setCharacterEncoding("unsupportedEncoding"); + + FlashMap flashMap = new FlashMap(); + flashMap.put("anyKey", "anyValue"); + + flashMap.addTargetRequestParam("ke+y1", "val+ue"); // contains white space(+) + flashMap.addTargetRequestParam("ke%20y2", "val%20ue"); // contains white space(%20) + + this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); + + MultiValueMap targetRequestParams = flashMap.getTargetRequestParams(); + assertEquals(Arrays.asList("val ue"), targetRequestParams.get("ke y1")); + assertEquals(Arrays.asList("val ue"), targetRequestParams.get("ke y2")); + } private static class TestFlashMapManager extends AbstractFlashMapManager {