diff --git a/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java index d110fe5d6a..7916e31195 100644 --- a/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java @@ -44,6 +44,7 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.util.Assert; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureAdapter; +import org.springframework.web.util.DefaultUriTemplateHandler; import org.springframework.web.util.UriTemplateHandler; /** @@ -150,6 +151,26 @@ public class AsyncRestTemplate extends InterceptingAsyncHttpAccessor implements return this.syncTemplate.getErrorHandler(); } + /** + * Configure default URI variable values. This is a shortcut for: + *
+	 *
+	 * DefaultUriTemplateHandler handler = new DefaultUriTemplateHandler();
+	 * handler.setDefaultUriVariables(...);
+	 *
+	 * AsyncRestTemplate restTemplate = new AsyncRestTemplate();
+	 * restTemplate.setUriTemplateHandler(handler);
+	 * 
+ * @param defaultUriVariables the default URI variable values + * @since 4.3 + */ + public void setDefaultUriVariables(Map defaultUriVariables) { + UriTemplateHandler handler = this.syncTemplate.getUriTemplateHandler(); + Assert.isInstanceOf(DefaultUriTemplateHandler.class, handler, + "Can only use this property in conjunction with a DefaultUriTemplateHandler."); + ((DefaultUriTemplateHandler) handler).setDefaultUriVariables(defaultUriVariables); + } + /** * This property has the same purpose as the corresponding property on the * {@code RestTemplate}. For more details see diff --git a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java index 34168d20f9..c700c8dae8 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java @@ -236,6 +236,25 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat return this.errorHandler; } + /** + * Configure default URI variable values. This is a shortcut for: + *
+	 *
+	 * DefaultUriTemplateHandler handler = new DefaultUriTemplateHandler();
+	 * handler.setDefaultUriVariables(...);
+	 *
+	 * RestTemplate restTemplate = new RestTemplate();
+	 * restTemplate.setUriTemplateHandler(handler);
+	 * 
+ * @param defaultUriVariables the default URI variable values + * @since 4.3 + */ + public void setDefaultUriVariables(Map defaultUriVariables) { + Assert.isInstanceOf(DefaultUriTemplateHandler.class, this.uriTemplateHandler, + "Can only use this property in conjunction with a DefaultUriTemplateHandler."); + ((DefaultUriTemplateHandler) this.uriTemplateHandler).setDefaultUriVariables(defaultUriVariables); + } + /** * Configure the {@link UriTemplateHandler} to use to expand URI templates. * By default the {@link DefaultUriTemplateHandler} is used which relies on 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 3a1555d448..57492c03f2 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 @@ -19,6 +19,7 @@ package org.springframework.web.util; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,6 +42,7 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler { private String baseUrl; + private final Map defaultUriVariables = new HashMap(); private boolean parsePath; @@ -72,6 +74,28 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler { return this.baseUrl; } + /** + * Configure default URI variable values to use with every expanded URI + * template. This default values apply only when expanding with a Map, and + * not with an array, where the Map supplied to expand can override the + * default values. + * @param defaultUriVariables the default URI variable values + * @since 4.3 + */ + public void setDefaultUriVariables(Map defaultUriVariables) { + this.defaultUriVariables.clear(); + if (defaultUriVariables != null) { + this.defaultUriVariables.putAll(defaultUriVariables); + } + } + + /** + * Return a read-only copy of the configured default URI variables. + */ + public Map getDefaultUriVariables() { + return Collections.unmodifiableMap(this.defaultUriVariables); + } + /** * Whether to parse the path of a URI template string into path segments. *

If set to {@code true} the URI template path is immediately decomposed @@ -152,15 +176,23 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler { } protected UriComponents expandAndEncode(UriComponentsBuilder builder, Map uriVariables) { - if (!isStrictEncoding()) { + // Simple scenario: use the input map + if (getDefaultUriVariables().isEmpty() && !isStrictEncoding()) { return builder.build().expand(uriVariables).encode(); } + // Create a new map + Map variablesToUse = new HashMap(); + variablesToUse.putAll(getDefaultUriVariables()); + variablesToUse.putAll(uriVariables); + + if (!isStrictEncoding()) { + return builder.build().expand(variablesToUse).encode(); + } else { - Map encodedUriVars = new HashMap(uriVariables.size()); - for (Map.Entry entry : uriVariables.entrySet()) { - encodedUriVars.put(entry.getKey(), applyStrictEncoding(entry.getValue())); + for (Map.Entry entry : variablesToUse.entrySet()) { + variablesToUse.put(entry.getKey(), applyStrictEncoding(entry.getValue())); } - return builder.build().expand(encodedUriVars); + return builder.build().expand(variablesToUse); } } diff --git a/spring-web/src/test/java/org/springframework/web/util/DefaultUriTemplateHandlerTests.java b/spring-web/src/test/java/org/springframework/web/util/DefaultUriTemplateHandlerTests.java index f650bcd295..09f3700a3c 100644 --- a/spring-web/src/test/java/org/springframework/web/util/DefaultUriTemplateHandlerTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/DefaultUriTemplateHandlerTests.java @@ -49,6 +49,22 @@ public class DefaultUriTemplateHandlerTests { assertEquals("http://localhost:8080/context/myapiresource", actual.toString()); } + @Test // SPR-14147 + public void defaultUriVariables() throws Exception { + Map defaultVars = new HashMap<>(2); + defaultVars.put("host", "api.example.com"); + defaultVars.put("port", "443"); + this.handler.setDefaultUriVariables(defaultVars); + + Map vars = new HashMap<>(1); + vars.put("id", 123L); + + String template = "https://{host}:{port}/v42/customers/{id}"; + URI actual = this.handler.expand(template, vars); + + assertEquals("https://api.example.com:443/v42/customers/123", actual.toString()); + } + @Test public void parsePathIsOff() throws Exception { this.handler.setParsePath(false); @@ -114,4 +130,20 @@ public class DefaultUriTemplateHandlerTests { assertEquals("http://www.example.com/user/john%3Bdoe/dashboard", actual.toString()); } + @Test // SPR-14147 + public void strictEncodingAndDefaultUriVariables() throws Exception { + Map defaultVars = new HashMap<>(1); + defaultVars.put("host", "www.example.com"); + this.handler.setDefaultUriVariables(defaultVars); + this.handler.setStrictEncoding(true); + + Map vars = new HashMap<>(1); + vars.put("userId", "john;doe"); + + String template = "http://{host}/user/{userId}/dashboard"; + URI actual = this.handler.expand(template, vars); + + assertEquals("http://www.example.com/user/john%3Bdoe/dashboard", actual.toString()); + } + }