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 428b8e5bad8..0cc18286424 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,6 +17,7 @@ package org.springframework.web.util; import java.net.URI; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -326,12 +327,24 @@ public class DefaultUriBuilderFactory implements UriBuilderFactory { return this; } + @Override + public DefaultUriBuilder queryParam(String name, @Nullable Collection values) { + this.uriComponentsBuilder.queryParam(name, values); + return this; + } + @Override public DefaultUriBuilder replaceQueryParam(String name, Object... values) { this.uriComponentsBuilder.replaceQueryParam(name, values); return this; } + @Override + public DefaultUriBuilder replaceQueryParam(String name, @Nullable Collection values) { + this.uriComponentsBuilder.replaceQueryParam(name, values); + return this; + } + @Override public DefaultUriBuilder queryParams(MultiValueMap params) { this.uriComponentsBuilder.queryParams(params); diff --git a/spring-web/src/main/java/org/springframework/web/util/UriBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriBuilder.java index f0b8f1dcc77..bcbb96e57b1 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -17,6 +17,7 @@ package org.springframework.web.util; import java.net.URI; +import java.util.Collection; import java.util.Map; import org.springframework.lang.Nullable; @@ -124,9 +125,22 @@ public interface UriBuilder { * only (i.e. {@code ?foo} instead of {@code ?foo=bar}. * @param name the query parameter name * @param values the query parameter values + * @see #queryParam(String, Collection) */ UriBuilder queryParam(String name, Object... values); + /** + * Append the given query parameter to the existing query parameters. The + * given name or any of the values may contain URI template variables. If no + * values are given, the resulting URI will contain the query parameter name + * only (i.e. {@code ?foo} instead of {@code ?foo=bar}. + * @param name the query parameter name + * @param values the query parameter values + * @since 5.2.0 + * @see #queryParam(String, Object...) + */ + UriBuilder queryParam(String name, @Nullable Collection values); + /** * Add the given query parameters. * @param params the params @@ -138,9 +152,20 @@ public interface UriBuilder { * the same parameter. If no values are given, the query parameter is removed. * @param name the query parameter name * @param values the query parameter values + * @see #replaceQueryParam(String, Collection) */ UriBuilder replaceQueryParam(String name, Object... values); + /** + * Set the query parameter values overriding all existing query values for + * the same parameter. If no values are given, the query parameter is removed. + * @param name the query parameter name + * @param values the query parameter values + * @since 5.2.0 + * @see #replaceQueryParam(String, Object...) + */ + UriBuilder replaceQueryParam(String name, @Nullable Collection values); + /** * Set the query parameter values overriding all existing query values. * @param params the query parameter name diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index 5e3db7e8ef0..99d7267d2c3 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -20,6 +20,7 @@ import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -100,6 +101,8 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { private static final Pattern FORWARDED_PROTO_PATTERN = Pattern.compile("proto=\"?([^;,\"]+)\"?"); + private static final Object[] EMPTY_VALUES = new Object[0]; + @Nullable private String scheme; @@ -696,6 +699,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { * @param name the query parameter name * @param values the query parameter values * @return this UriComponentsBuilder + * @see #queryParam(String, Collection) */ @Override public UriComponentsBuilder queryParam(String name, Object... values) { @@ -713,6 +717,22 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { return this; } + /** + * Append the given query parameter to the existing query parameters. The + * given name or any of the values may contain URI template variables. If no + * values are given, the resulting URI will contain the query parameter name + * only (i.e. {@code ?foo} instead of {@code ?foo=bar}). + * @param name the query parameter name + * @param values the query parameter values + * @return this UriComponentsBuilder + * @since 5.2.0 + * @see #queryParam(String, Object...) + */ + @Override + public UriComponentsBuilder queryParam(String name, @Nullable Collection values) { + return queryParam(name, values != null ? values.toArray() : EMPTY_VALUES); + } + /** * Add the given query parameters. * @param params the params @@ -733,6 +753,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { * @param name the query parameter name * @param values the query parameter values * @return this UriComponentsBuilder + * @see #replaceQueryParam(String, Collection) */ @Override public UriComponentsBuilder replaceQueryParam(String name, Object... values) { @@ -745,6 +766,20 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { return this; } + /** + * Set the query parameter values overriding all existing query values for + * the same parameter. If no values are given, the query parameter is removed. + * @param name the query parameter name + * @param values the query parameter values + * @return this UriComponentsBuilder + * @see #replaceQueryParam(String, Object...) + * @since 5.2.0 + */ + @Override + public UriComponentsBuilder replaceQueryParam(String name, @Nullable Collection values) { + return replaceQueryParam(name, values != null ? values.toArray() : EMPTY_VALUES); + } + /** * Set the query parameter values overriding all existing query values. * @param params the query parameter name diff --git a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java index c580a5beb9d..761bc68cfe9 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java @@ -19,6 +19,7 @@ package org.springframework.web.util; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -643,9 +644,20 @@ public class UriComponentsBuilderTests { } @Test - public void queryParams() { + public void queryParam() { + UriComponents result = UriComponentsBuilder.newInstance().queryParam("baz", "qux", 42).build(); + + assertThat(result.getQuery()).isEqualTo("baz=qux&baz=42"); + MultiValueMap expectedQueryParams = new LinkedMultiValueMap<>(2); + expectedQueryParams.add("baz", "qux"); + expectedQueryParams.add("baz", "42"); + assertThat(result.getQueryParams()).isEqualTo(expectedQueryParams); + } + + @Test + public void queryParamWithList() { UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); - UriComponents result = builder.queryParam("baz", "qux", 42).build(); + UriComponents result = builder.queryParam("baz", Arrays.asList("qux", 42)).build(); assertThat(result.getQuery()).isEqualTo("baz=qux&baz=42"); MultiValueMap expectedQueryParams = new LinkedMultiValueMap<>(2); @@ -665,6 +677,20 @@ public class UriComponentsBuilderTests { assertThat(result.getQueryParams()).isEqualTo(expectedQueryParams); } + @Test + public void emptyQueryParams() { + UriComponents result = UriComponentsBuilder.newInstance() + .queryParam("baz", Collections.emptyList()) + .queryParam("foo", (Collection) null) + .build(); + + assertThat(result.getQuery()).isEqualTo("baz&foo"); + MultiValueMap expectedQueryParams = new LinkedMultiValueMap<>(2); + expectedQueryParams.add("baz", null); + expectedQueryParams.add("foo", null); + assertThat(result.getQueryParams()).isEqualTo(expectedQueryParams); + } + @Test public void replaceQueryParam() { UriComponentsBuilder builder = UriComponentsBuilder.newInstance().queryParam("baz", "qux", 42); @@ -680,6 +706,21 @@ public class UriComponentsBuilderTests { assertThat(result.getQuery()).as("Query param should have been deleted").isNull(); } + @Test + public void replaceQueryParams() { + UriComponentsBuilder builder = UriComponentsBuilder.newInstance().queryParam("baz", Arrays.asList("qux", 42)); + builder.replaceQueryParam("baz", Arrays.asList("xuq", 24)); + UriComponents result = builder.build(); + + assertThat(result.getQuery()).isEqualTo("baz=xuq&baz=24"); + + builder = UriComponentsBuilder.newInstance().queryParam("baz", Arrays.asList("qux", 42)); + builder.replaceQueryParam("baz", Collections.emptyList()); + result = builder.build(); + + assertThat(result.getQuery()).as("Query param should have been deleted").isNull(); + } + @Test public void buildAndExpandHierarchical() { UriComponents result = UriComponentsBuilder.fromPath("/{foo}").buildAndExpand("fooValue");