Support strict URI variable encoding
The DefaulUriTemplateHandler now provides a strictEncoding property which if turned on encodes everything outside the reserved char set. This is in contrast to the default policy of encoding only illegal charaters depending on the URI component type. Issue: SPR-11652
This commit is contained in:
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.springframework.web.util;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -36,6 +38,8 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler {
|
||||
|
||||
private boolean parsePath;
|
||||
|
||||
private boolean strictEncoding;
|
||||
|
||||
|
||||
/**
|
||||
* Configure a base URL to prepend URI templates with. The base URL must
|
||||
@@ -83,19 +87,45 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler {
|
||||
return this.parsePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to encode characters outside the unreserved set as defined in
|
||||
* <a href="https://tools.ietf.org/html/rfc3986#section-2">RFC 3986 Section 2</a>.
|
||||
* This ensures a URI variable value will not contain any characters with a
|
||||
* reserved purpose.
|
||||
* <p>By default this is set to {@code false} in which case only characters
|
||||
* illegal for the given URI component are encoded. For example when expanding
|
||||
* a URI variable into a path segment the "/" character is illegal and
|
||||
* encoded. The ";" character however is legal and not encoded even though
|
||||
* it has a reserved purpose.
|
||||
* <p><strong>Note:</strong> this property supersedes the need to also set
|
||||
* the {@link #setParsePath parsePath} property.
|
||||
* @param strictEncoding whether to perform strict encoding
|
||||
* @since 4.3
|
||||
*/
|
||||
public void setStrictEncoding(boolean strictEncoding) {
|
||||
this.strictEncoding = strictEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to strictly encode any character outside the unreserved set.
|
||||
*/
|
||||
public boolean isStrictEncoding() {
|
||||
return this.strictEncoding;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public URI expand(String uriTemplate, Map<String, ?> uriVariables) {
|
||||
UriComponentsBuilder builder = initUriComponentsBuilder(uriTemplate);
|
||||
UriComponents url = builder.build().expand(uriVariables).encode();
|
||||
return insertBaseUrl(url);
|
||||
UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate);
|
||||
UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables);
|
||||
return insertBaseUrl(uriComponents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI expand(String uriTemplate, Object... uriVariables) {
|
||||
UriComponentsBuilder builder = initUriComponentsBuilder(uriTemplate);
|
||||
UriComponents url = builder.build().expand(uriVariables).encode();
|
||||
return insertBaseUrl(url);
|
||||
UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate);
|
||||
UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables);
|
||||
return insertBaseUrl(uriComponents);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +135,7 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler {
|
||||
*/
|
||||
protected UriComponentsBuilder initUriComponentsBuilder(String uriTemplate) {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uriTemplate);
|
||||
if (shouldParsePath()) {
|
||||
if (shouldParsePath() && !isStrictEncoding()) {
|
||||
List<String> pathSegments = builder.build().getPathSegments();
|
||||
builder.replacePath(null);
|
||||
for (String pathSegment : pathSegments) {
|
||||
@@ -115,6 +145,43 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler {
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected UriComponents expandAndEncode(UriComponentsBuilder builder, Map<String, ?> uriVariables) {
|
||||
if (!isStrictEncoding()) {
|
||||
return builder.build().expand(uriVariables).encode();
|
||||
}
|
||||
else {
|
||||
Map<String, Object> encodedUriVars = new HashMap<String, Object>(uriVariables.size());
|
||||
for (Map.Entry<String, ?> entry : uriVariables.entrySet()) {
|
||||
encodedUriVars.put(entry.getKey(), encodeValue(entry.getValue()));
|
||||
}
|
||||
return builder.build().expand(encodedUriVars);
|
||||
}
|
||||
}
|
||||
|
||||
protected UriComponents expandAndEncode(UriComponentsBuilder builder, Object[] uriVariables) {
|
||||
if (!isStrictEncoding()) {
|
||||
return builder.build().expand(uriVariables).encode();
|
||||
}
|
||||
else {
|
||||
Object[] encodedUriVars = new Object[uriVariables.length];
|
||||
for (int i = 0; i < uriVariables.length; i++) {
|
||||
encodedUriVars[i] = encodeValue(uriVariables[i]);
|
||||
}
|
||||
return builder.build().expand(encodedUriVars);
|
||||
}
|
||||
}
|
||||
|
||||
private String encodeValue(Object value) {
|
||||
String stringValue = (value != null ? value.toString() : "");
|
||||
try {
|
||||
return UriUtils.encode(stringValue, "UTF-8");
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
// Should never happen
|
||||
throw new IllegalStateException("Failed to encode URI variable", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked after the URI template has been expanded and encoded to prepend
|
||||
* the configured {@link #setBaseUrl(String) baseUrl} if any.
|
||||
@@ -122,10 +189,10 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler {
|
||||
* @return the final URI
|
||||
*/
|
||||
protected URI insertBaseUrl(UriComponents uriComponents) {
|
||||
if (getBaseUrl() == null || uriComponents.getHost() != null) {
|
||||
return uriComponents.toUri();
|
||||
String url = uriComponents.toUriString();
|
||||
if (getBaseUrl() != null && uriComponents.getHost() == null) {
|
||||
url = getBaseUrl() + url;
|
||||
}
|
||||
String url = getBaseUrl() + uriComponents.toUriString();
|
||||
try {
|
||||
return new URI(url);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user