From 3607f982402eb0fe271fad3436a775d0fd1fee38 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Wed, 15 Oct 2025 12:30:29 +0100 Subject: [PATCH] Refine null handling in UriComponentsBuilder#query Closes gh-35629 --- .../springframework/web/util/UriBuilder.java | 7 ++++--- .../web/util/UriComponentsBuilder.java | 12 +++++++---- .../web/util/UriComponentsBuilderTests.java | 20 +++++++++---------- 3 files changed, 21 insertions(+), 18 deletions(-) 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 3a57ab91ba7..4b033027bbb 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 @@ -144,9 +144,10 @@ public interface UriBuilder { UriBuilder pathSegment(String... pathSegments) throws IllegalArgumentException; /** - * Parse the given query string into query parameters where parameters are - * separated with {@code '&'} and their values, if any, with {@code '='}. - * The query may contain URI template variables. + * Parse the given query string into query parameters, and append them to + * the query string. Query parameters are separated with {@code '&'} while + * their values, if any, are separated with {@code '='}. The query string + * may contain URI template variables. *

Note: please, review the Javadoc of * {@link #queryParam(String, Object...)} for further notes on the treatment * and encoding of individual query parameters. 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 21acd3d1f46..e8783e4cf2e 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 @@ -426,7 +426,9 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { if (record.path() != null) { path(record.path()); } - query(record.query()); + if (record.query() != null) { + query(record.query()); + } } fragment(record.fragment()); return this; @@ -453,7 +455,9 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { port(record.portString()); } path(record.path().toString()); - query(record.query()); + if (record.query() != null) { + query(record.query()); + } } if (StringUtils.hasText(record.fragment())) { fragment(record.fragment()); @@ -541,8 +545,8 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { } @Override - public UriComponentsBuilder query(@Nullable String query) { - if (query != null) { + public UriComponentsBuilder query(String query) { + if (StringUtils.hasText(query)) { Matcher matcher = QUERY_PARAM_PATTERN.matcher(query); while (matcher.find()) { String name = matcher.group(1); 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 b2edb42017f..991b91801d7 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 @@ -479,20 +479,18 @@ class UriComponentsBuilderTests { @ParameterizedTest @EnumSource - void query(final ParserType parserType) { - final UriComponents uriComponents = UriComponentsBuilder.fromUriString("https://example.com/foo?foo=bar", parserType) - .query("baz=qux") - .build(); - assertThat(uriComponents.getQueryParams()).isEqualTo(Map.of("foo", List.of("bar"), "baz", List.of("qux"))); + void query(ParserType parserType) { + String url = "https://example.com/foo?foo=bar"; + UriComponents uric = UriComponentsBuilder.fromUriString(url, parserType).query("baz=qux").build(); + assertThat(uric.getQueryParams()).isEqualTo(Map.of("foo", List.of("bar"), "baz", List.of("qux"))); } @ParameterizedTest - @EnumSource - void queryWithNullDoesRetainQueryParameters(final ParserType parserType) { - final UriComponents uriComponents = UriComponentsBuilder.fromUriString("https://example.com/foo?foo=bar", parserType) - .query(null) - .build(); - assertThat(uriComponents.getQueryParams()).isEqualTo(Map.of("foo", List.of("bar"))); + @EnumSource // gh-35628 + void queryWithNull(ParserType parserType) { + String url = "https://example.com/foo?foo=bar"; + UriComponents uric = UriComponentsBuilder.fromUriString(url, parserType).query(null).build(); + assertThat(uric.getQueryParams()).isEqualTo(Map.of("foo", List.of("bar"))); } @ParameterizedTest