Skip to content

Commit

Permalink
Additional convenience methods in UriUtils
Browse files Browse the repository at this point in the history
The generic encode method in UriUtils that encodes any character
outside the reserved character set for a URI is meant for "strict"
encoding of URI variable values. This commit adds a couple more
conveninence methods that accept a Map or array of URI variable
values to encode.

This facilitates the use case where the URI template is assumed to
be encoded while URI variables are encoded strictly to avoid any
possibility for unwanted reserved characters:

Map<String, ?> encodedUriVars = UriUtils.encodeUriVariables(uriVars);
uriComponentsBuilder.build(true).expand(encodedUriVars).toUri();

Issue: SPR-14970
  • Loading branch information
rstoyanchev committed Jan 19, 2017
1 parent f2e293a commit bb3b1f2
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 22 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,10 +16,8 @@

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;

Expand Down Expand Up @@ -127,10 +125,7 @@ protected UriComponents expandAndEncode(UriComponentsBuilder builder, Map<String
return builder.buildAndExpand(uriVariables).encode();
}
else {
Map<String, Object> encodedUriVars = new HashMap<>(uriVariables.size());
for (Map.Entry<String, ?> entry : uriVariables.entrySet()) {
encodedUriVars.put(entry.getKey(), applyStrictEncoding(entry.getValue()));
}
Map<String, ?> encodedUriVars = UriUtils.encodeUriVariables(uriVariables);
return builder.buildAndExpand(encodedUriVars);
}
}
Expand All @@ -140,25 +135,11 @@ protected UriComponents expandAndEncode(UriComponentsBuilder builder, Object[] u
return builder.buildAndExpand(uriVariables).encode();
}
else {
Object[] encodedUriVars = new Object[uriVariables.length];
for (int i = 0; i < uriVariables.length; i++) {
encodedUriVars[i] = applyStrictEncoding(uriVariables[i]);
}
Object[] encodedUriVars = UriUtils.encodeUriVariables(uriVariables);
return builder.buildAndExpand(encodedUriVars);
}
}

private String applyStrictEncoding(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);
}
}

private URI createUri(UriComponents uriComponents) {
try {
// Avoid further encoding (in the case of strictEncoding=true)
Expand Down
Expand Up @@ -18,6 +18,11 @@

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.util.StringUtils;

Expand Down Expand Up @@ -165,6 +170,52 @@ public static String encode(String source, String encoding) throws UnsupportedEn
return HierarchicalUriComponents.encodeUriComponent(source, encoding, type);
}

/**
* Encode characters outside the unreserved character set as defined in
* <a href="https://tools.ietf.org/html/rfc3986#section-2">RFC 3986 Section 2</a>.
* <p>This can be used to ensure the given String will not contain any
* characters with reserved URI meaning regardless of URI component.
* @param source the String to be encoded
* @param charset the character encoding to encode to
* @return the encoded String
*/
public static String encode(String source, Charset charset) {
HierarchicalUriComponents.Type type = HierarchicalUriComponents.Type.URI;
return HierarchicalUriComponents.encodeUriComponent(source, charset, type);
}

/**
* Apply {@link #encode(String, String)} to the values in the given URI
* variables and return a new Map containing the encoded values.
* @param uriVariables the URI variable values to be encoded
* @return the encoded String
* @since 5.0
*/
public static Map<String, String> encodeUriVariables(Map<String, ?> uriVariables) {
Map<String, String> result = new LinkedHashMap<>(uriVariables.size());
uriVariables.entrySet().stream().forEach(entry -> {
String stringValue = (entry.getValue() != null ? entry.getValue().toString() : "");
result.put(entry.getKey(), encode(stringValue, StandardCharsets.UTF_8));
});
return result;
}

/**
* Apply {@link #encode(String, String)} to the values in the given URI
* variables and return a new array containing the encoded values.
* @param uriVariables the URI variable values to be encoded
* @return the encoded String
* @since 5.0
*/
public static Object[] encodeUriVariables(Object... uriVariables) {
return Arrays.stream(uriVariables)
.map(value -> {
String stringValue = (value != null ? value.toString() : "");
return encode(stringValue, StandardCharsets.UTF_8);
})
.collect(Collectors.toList()).toArray();
}

/**
* Decode the given encoded URI component.
* <p>See {@link StringUtils#uriDecode(String, Charset) for the decoding rules.
Expand Down

0 comments on commit bb3b1f2

Please sign in to comment.