/currentApplicationContext/url/path/more+than+JSTL+c%3Aurl
+ * /currentApplicationContext/url/path/more%20than%20JSTL%20c%3Aurl
*
* @author Scott Andrews
* @since 3.0
@@ -269,7 +271,6 @@ public class UrlTag extends HtmlEscapingAwareTag implements ParamAware {
*/
protected String replaceUriTemplateParams(String uri, List params, SetThis method will not URL-encode according to the
+ * application/x-www-form-urlencoded
MIME format. Spaces will
+ * encoded as regular character instead of +
. In UTF-8
+ * a space encodes to %20
.
* @param value the value to encode
* @return the URL encoded value
* @throws JspException if the character encoding is invalid
@@ -291,13 +296,41 @@ public class UrlTag extends HtmlEscapingAwareTag implements ParamAware {
return null;
}
try {
- return URLEncoder.encode(value, pageContext.getResponse().getCharacterEncoding());
+ String encoding = pageContext.getResponse().getCharacterEncoding();
+ String formUrlEncodedValue = URLEncoder.encode(value, encoding);
+ if (!formUrlEncodedValue.contains("+")) {
+ return formUrlEncodedValue;
+ }
+ String spaceEncoding = this.urlEncode(' ', encoding);
+ return formUrlEncodedValue.replace("+", spaceEncoding);
}
catch (UnsupportedEncodingException ex) {
throw new JspException(ex);
}
}
+ /*
+ * based on URLCodec from Apache Commons Codec
+ */
+ protected String urlEncode(Character c, String enc) throws UnsupportedEncodingException {
+ if (c == null) {
+ return null;
+ }
+ byte[] bytes = c.toString().getBytes(enc);
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < bytes.length; i++) {
+ int b = bytes[i];
+ if (b < 0) {
+ b = 256 + b;
+ }
+ char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
+ char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
+ builder.append('%');
+ builder.append(hex1);
+ builder.append(hex2);
+ }
+ return builder.toString();
+ }
/**
* Internal enum that classifies URLs by type.
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/UrlTagTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/UrlTagTests.java
index 9477c7947c..e60ea953a4 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/UrlTagTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/UrlTagTests.java
@@ -16,7 +16,9 @@
package org.springframework.web.servlet.tags;
+import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
+import java.net.URLDecoder;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -108,7 +110,7 @@ public class UrlTagTests extends AbstractTagTests {
tag.doEndTag();
- assertEquals("url/path?n+me=v%26l%3De&name=value2", context
+ assertEquals("url/path?n%20me=v%26l%3De&name=value2", context
.getAttribute("var"));
}
@@ -131,7 +133,7 @@ public class UrlTagTests extends AbstractTagTests {
tag.doEndTag();
- assertEquals("url/path?n+me=v%26l%3De&name=value2", context
+ assertEquals("url/path?n%20me=v%26l%3De&name=value2", context
.getAttribute("var"));
}
@@ -154,7 +156,7 @@ public class UrlTagTests extends AbstractTagTests {
tag.doEndTag();
- assertEquals("url/path?n+me=v%26l%3De&name=value2", context
+ assertEquals("url/path?n%20me=v%26l%3De&name=value2", context
.getAttribute("var"));
}
@@ -177,7 +179,7 @@ public class UrlTagTests extends AbstractTagTests {
tag.doEndTag();
- assertEquals("url\\/path?n+me=v%26l%3De&name=value2", context
+ assertEquals("url\\/path?n%20me=v%26l%3De&name=value2", context
.getAttribute("var"));
}
@@ -201,7 +203,7 @@ public class UrlTagTests extends AbstractTagTests {
tag.doEndTag();
- assertEquals("url\\/path?n+me=v%26l%3De&name=value2", context
+ assertEquals("url\\/path?n%20me=v%26l%3De&name=value2", context
.getAttribute("var"));
}
@@ -322,7 +324,7 @@ public class UrlTagTests extends AbstractTagTests {
String queryString = tag.createQueryString(params, usedParams, true);
- assertEquals("?n+me=v%26l%3De&name=value2", queryString);
+ assertEquals("?n%20me=v%26l%3De&name=value2", queryString);
}
public void testCreateQueryStringParamNullName() throws JspException {
@@ -425,7 +427,7 @@ public class UrlTagTests extends AbstractTagTests {
String uri = tag.replaceUriTemplateParams("url/{name}", params,
usedParams);
- assertEquals("url/v+lue", uri);
+ assertEquals("url/v%20lue", uri);
assertEquals(1, usedParams.size());
assertTrue(usedParams.contains("name"));
}
@@ -509,7 +511,7 @@ public class UrlTagTests extends AbstractTagTests {
String uri = invokeCreateUrl(tag);
- assertEquals("url/path?name=value&n+me=v+lue", uri);
+ assertEquals("url/path?name=value&n%20me=v%20lue", uri);
}
public void testCreateUrlWithTemplateParams() throws JspException {
@@ -529,7 +531,7 @@ public class UrlTagTests extends AbstractTagTests {
String uri = invokeCreateUrl(tag);
- assertEquals("url/value?n+me=v+lue", uri);
+ assertEquals("url/value?n%20me=v%20lue", uri);
}
public void testCreateUrlWithParamAndExsistingQueryString()
@@ -549,7 +551,14 @@ public class UrlTagTests extends AbstractTagTests {
}
public void testUrlEncode() throws JspException {
- assertEquals("my+name", tag.urlEncode("my name"));
+ assertEquals("my%20name%2Bis", tag.urlEncode("my name+is"));
+ }
+
+ public void testUrlEncode_character() throws UnsupportedEncodingException {
+ assertEquals("%20", tag.urlEncode(' ', "UTF-8"));
+ assertEquals(" ", URLDecoder.decode("%20", "UTF-8"));
+ assertEquals("%FE%FF%00%20", tag.urlEncode(' ', "UTF-16"));
+ assertEquals("%40", tag.urlEncode(' ', "IBM-Thai"));
}
public void testUrlEncodeNull() throws JspException {