diff --git a/spring-web/src/main/java/org/springframework/web/util/DefaultUriBuilderFactory.java b/spring-web/src/main/java/org/springframework/web/util/DefaultUriBuilderFactory.java index dbfff83176..d68dd93c28 100644 --- a/spring-web/src/main/java/org/springframework/web/util/DefaultUriBuilderFactory.java +++ b/spring-web/src/main/java/org/springframework/web/util/DefaultUriBuilderFactory.java @@ -17,7 +17,6 @@ package org.springframework.web.util; import java.net.URI; -import java.nio.charset.Charset; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -49,16 +48,33 @@ public class DefaultUriBuilderFactory implements UriBuilderFactory { public enum EncodingMode { /** - * Apply strict encoding to URI variables at the time of expanding, - * quoting both illegal characters and characters with reserved meaning - * via {@link UriUtils#encode(String, Charset)}. + * Encode the URI template first, and URI variables later when expanded, + * applying the following to each: + * + *

For most cases this should be the preferred encoding mode. + * @since 5.0.8 + * @see UriComponentsBuilder#encode() + */ + TEMPLATE_AND_VALUES, + + /** + * Encode only URI variables strictly quoting both illegal characters + * and characters with reserved meaning. + * @see UriUtils#encodeUriVariables(Object...) + * @see UriUtils#encodeUriVariables(Map) */ VALUES_ONLY, /** - * Expand URI variables first, then encode the resulting URI component + * Expand URI variables first, and then encode the expanded URI component * values, quoting only illegal characters within a given URI * component type, but not all characters with reserved meaning. + * @see UriComponents#encode() */ URI_COMPONENT, @@ -110,7 +126,7 @@ public class DefaultUriBuilderFactory implements UriBuilderFactory { /** - * Specify the {@link EncodingMode EncodingMode} to use when building URIs. + * Specify the {@link EncodingMode EncodingMode} to use to encode URIs. *

By default set to * {@link EncodingMode#URI_COMPONENT EncodingMode.URI_COMPONENT}. * @param encodingMode the encoding mode to use @@ -216,13 +232,17 @@ public class DefaultUriBuilderFactory implements UriBuilderFactory { result = UriComponentsBuilder.fromUriString(uriTemplate); } + if (encodingMode.equals(EncodingMode.TEMPLATE_AND_VALUES)) { + result.encode(); + } + parsePathIfNecessary(result); return result; } private void parsePathIfNecessary(UriComponentsBuilder result) { - if (shouldParsePath() && encodingMode.equals(EncodingMode.URI_COMPONENT)) { + if (parsePath && encodingMode.equals(EncodingMode.URI_COMPONENT)) { UriComponents uric = result.build(); String path = uric.getPath(); result.replacePath(null); diff --git a/spring-web/src/test/java/org/springframework/web/util/DefaultUriBuilderFactoryTests.java b/spring-web/src/test/java/org/springframework/web/util/DefaultUriBuilderFactoryTests.java index af2fbae915..1f42b32f34 100644 --- a/spring-web/src/test/java/org/springframework/web/util/DefaultUriBuilderFactoryTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/DefaultUriBuilderFactoryTests.java @@ -97,6 +97,22 @@ public class DefaultUriBuilderFactoryTests { assertEquals("https://api.example.com:443/v42/customers/123", uri.toString()); } + @Test + public void encodeTemplateAndValues() { + DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(); + factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES); + UriBuilder uriBuilder = factory.uriString("/hotel list/{city} specials?q={value}"); + + String expected = "/hotel%20list/Z%C3%BCrich%20specials?q=a%2Bb"; + + Map vars = new HashMap<>(); + vars.put("city", "Z\u00fcrich"); + vars.put("value", "a+b"); + + assertEquals(expected, uriBuilder.build("Z\u00fcrich", "a+b").toString()); + assertEquals(expected, uriBuilder.build(vars).toString()); + } + @Test public void encodingValuesOnly() { DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory();