diff --git a/spring-web/src/main/java/org/springframework/web/util/DefaultUriTemplateHandler.java b/spring-web/src/main/java/org/springframework/web/util/DefaultUriTemplateHandler.java index b6e3ea69001..8c0ad71504f 100644 --- a/spring-web/src/main/java/org/springframework/web/util/DefaultUriTemplateHandler.java +++ b/spring-web/src/main/java/org/springframework/web/util/DefaultUriTemplateHandler.java @@ -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. @@ -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; @@ -127,10 +125,7 @@ public class DefaultUriTemplateHandler extends AbstractUriTemplateHandler { return builder.buildAndExpand(uriVariables).encode(); } else { - Map encodedUriVars = new HashMap<>(uriVariables.size()); - for (Map.Entry entry : uriVariables.entrySet()) { - encodedUriVars.put(entry.getKey(), applyStrictEncoding(entry.getValue())); - } + Map encodedUriVars = UriUtils.encodeUriVariables(uriVariables); return builder.buildAndExpand(encodedUriVars); } } @@ -140,25 +135,11 @@ public class DefaultUriTemplateHandler extends AbstractUriTemplateHandler { 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) diff --git a/spring-web/src/main/java/org/springframework/web/util/UriUtils.java b/spring-web/src/main/java/org/springframework/web/util/UriUtils.java index 7cb3b76f1e2..7c010f02912 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriUtils.java @@ -18,6 +18,11 @@ package org.springframework.web.util; 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; @@ -165,6 +170,52 @@ public abstract class UriUtils { return HierarchicalUriComponents.encodeUriComponent(source, encoding, type); } + /** + * Encode characters outside the unreserved character set as defined in + * RFC 3986 Section 2. + *

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 encodeUriVariables(Map uriVariables) { + Map 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. *

See {@link StringUtils#uriDecode(String, Charset) for the decoding rules.