diff --git a/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java b/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java index 25889ec448..e37aa40b10 100644 --- a/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java @@ -133,6 +133,8 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { /** * Alternative to {@link #method(HttpMethod, URI)} that accepts a URI template. + * The given URI may contain query parameters, or those may be added later via + * {@link BaseBuilder#queryParam queryParam} builder methods. * @param method the HTTP method (GET, POST, etc) * @param urlTemplate the URL template * @param vars variables to expand into the template @@ -144,7 +146,9 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP GET builder with the given url. + * Create an HTTP GET builder with the given URI template. The given URI may + * contain query parameters, or those may be added later via + * {@link BaseBuilder#queryParam queryParam} builder methods. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -154,7 +158,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP HEAD builder with the given url. + * HTTP HEAD variant. See {@link #get(String, Object...)} for general info. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -164,7 +168,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP POST builder with the given url. + * HTTP POST variant. See {@link #get(String, Object...)} for general info. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -174,7 +178,8 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP PUT builder with the given url. + * HTTP PUT variant. See {@link #get(String, Object...)} for general info. + * {@link BaseBuilder#queryParam queryParam} builder methods. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -184,7 +189,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP PATCH builder with the given url. + * HTTP PATCH variant. See {@link #get(String, Object...)} for general info. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -194,7 +199,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP DELETE builder with the given url. + * HTTP DELETE variant. See {@link #get(String, Object...)} for general info. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -204,7 +209,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Creates an HTTP OPTIONS builder with the given url. + * HTTP OPTIONS variant. See {@link #get(String, Object...)} for general info. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -225,6 +230,25 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { */ B contextPath(String contextPath); + /** + * Append the given query parameter to the existing query parameters. + * If no values are given, the resulting URI will contain the query + * parameter name only (i.e. {@code ?foo} instead of {@code ?foo=bar}). + *

The provided query name and values will be encoded. + * @param name the query parameter name + * @param values the query parameter values + * @return this UriComponentsBuilder + */ + B queryParam(String name, Object... values); + + /** + * Add the given query parameters and values. The provided query name + * and corresponding values will be encoded. + * @param params the params + * @return this UriComponentsBuilder + */ + B queryParams(MultiValueMap params); + /** * Set the remote address to return. */ @@ -375,6 +399,8 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { @Nullable private String contextPath; + private final UriComponentsBuilder queryParamsBuilder = UriComponentsBuilder.newInstance(); + private final HttpHeaders headers = new HttpHeaders(); private final MultiValueMap cookies = new LinkedMultiValueMap<>(); @@ -397,6 +423,18 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { return this; } + @Override + public BodyBuilder queryParam(String name, Object... values) { + this.queryParamsBuilder.queryParam(name, values); + return this; + } + + @Override + public BodyBuilder queryParams(MultiValueMap params) { + this.queryParamsBuilder.queryParams(params); + return this; + } + @Override public BodyBuilder remoteAddress(InetSocketAddress remoteAddress) { this.remoteAddress = remoteAddress; @@ -506,7 +544,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { @Override public MockServerHttpRequest body(Publisher body) { applyCookiesIfNecessary(); - return new MockServerHttpRequest(this.method, this.url, this.contextPath, + return new MockServerHttpRequest(this.method, getUrlToUse(), this.contextPath, this.headers, this.cookies, this.remoteAddress, this.sslInfo, body); } @@ -516,6 +554,17 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { .forEach(cookie -> this.headers.add(HttpHeaders.COOKIE, cookie.toString())); } } + + private URI getUrlToUse() { + MultiValueMap params = + this.queryParamsBuilder.buildAndExpand().encode().getQueryParams(); + + if (!params.isEmpty()) { + return UriComponentsBuilder.fromUri(this.url).queryParams(params).build(true).toUri(); + } + + return this.url; + } } } \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/mock/http/server/reactive/MockServerHttpRequestTests.java b/spring-test/src/test/java/org/springframework/mock/http/server/reactive/MockServerHttpRequestTests.java index dc8f6e148b..c7ecb6e350 100644 --- a/spring-test/src/test/java/org/springframework/mock/http/server/reactive/MockServerHttpRequestTests.java +++ b/spring-test/src/test/java/org/springframework/mock/http/server/reactive/MockServerHttpRequestTests.java @@ -30,7 +30,6 @@ import static org.junit.Assert.assertEquals; */ public class MockServerHttpRequestTests { - @Test public void cookieHeaderSet() throws Exception { HttpCookie foo11 = new HttpCookie("foo1", "bar1"); @@ -47,4 +46,15 @@ public class MockServerHttpRequestTests { request.getHeaders().get(HttpHeaders.COOKIE)); } + @Test + public void queryParams() throws Exception { + MockServerHttpRequest request = MockServerHttpRequest.get("/foo bar?a=b") + .queryParam("name A", "value A1", "value A2") + .queryParam("name B", "value B1") + .build(); + + assertEquals("/foo%20bar?a=b&name%20A=value%20A1&name%20A=value%20A2&name%20B=value%20B1", + request.getURI().toString()); + } + } 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 6ab0892cd8..0f92c40c02 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 @@ -632,7 +632,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { * 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}. + * 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 diff --git a/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java b/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java index ffe448ec23..ed4e9b8311 100644 --- a/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java +++ b/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java @@ -133,6 +133,8 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { /** * Alternative to {@link #method(HttpMethod, URI)} that accepts a URI template. + * The given URI may contain query parameters, or those may be added later via + * {@link BaseBuilder#queryParam queryParam} builder methods. * @param method the HTTP method (GET, POST, etc) * @param urlTemplate the URL template * @param vars variables to expand into the template @@ -144,7 +146,9 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP GET builder with the given url. + * Create an HTTP GET builder with the given URI template. The given URI may + * contain query parameters, or those may be added later via + * {@link BaseBuilder#queryParam queryParam} builder methods. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -154,7 +158,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP HEAD builder with the given url. + * HTTP HEAD variant. See {@link #get(String, Object...)} for general info. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -164,7 +168,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP POST builder with the given url. + * HTTP POST variant. See {@link #get(String, Object...)} for general info. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -174,7 +178,8 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP PUT builder with the given url. + * HTTP PUT variant. See {@link #get(String, Object...)} for general info. + * {@link BaseBuilder#queryParam queryParam} builder methods. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -184,7 +189,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP PATCH builder with the given url. + * HTTP PATCH variant. See {@link #get(String, Object...)} for general info. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -194,7 +199,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Create an HTTP DELETE builder with the given url. + * HTTP DELETE variant. See {@link #get(String, Object...)} for general info. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -204,7 +209,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { } /** - * Creates an HTTP OPTIONS builder with the given url. + * HTTP OPTIONS variant. See {@link #get(String, Object...)} for general info. * @param urlTemplate a URL template; the resulting URL will be encoded * @param uriVars zero or more URI variables * @return the created builder @@ -225,6 +230,25 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { */ B contextPath(String contextPath); + /** + * Append the given query parameter to the existing query parameters. + * If no values are given, the resulting URI will contain the query + * parameter name only (i.e. {@code ?foo} instead of {@code ?foo=bar}). + *

The provided query name and values will be encoded. + * @param name the query parameter name + * @param values the query parameter values + * @return this UriComponentsBuilder + */ + B queryParam(String name, Object... values); + + /** + * Add the given query parameters and values. The provided query name + * and corresponding values will be encoded. + * @param params the params + * @return this UriComponentsBuilder + */ + B queryParams(MultiValueMap params); + /** * Set the remote address to return. */ @@ -375,6 +399,8 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { @Nullable private String contextPath; + private final UriComponentsBuilder queryParamsBuilder = UriComponentsBuilder.newInstance(); + private final HttpHeaders headers = new HttpHeaders(); private final MultiValueMap cookies = new LinkedMultiValueMap<>(); @@ -397,6 +423,18 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { return this; } + @Override + public BodyBuilder queryParam(String name, Object... values) { + this.queryParamsBuilder.queryParam(name, values); + return this; + } + + @Override + public BodyBuilder queryParams(MultiValueMap params) { + this.queryParamsBuilder.queryParams(params); + return this; + } + @Override public BodyBuilder remoteAddress(InetSocketAddress remoteAddress) { this.remoteAddress = remoteAddress; @@ -506,7 +544,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { @Override public MockServerHttpRequest body(Publisher body) { applyCookiesIfNecessary(); - return new MockServerHttpRequest(this.method, this.url, this.contextPath, + return new MockServerHttpRequest(this.method, getUrlToUse(), this.contextPath, this.headers, this.cookies, this.remoteAddress, this.sslInfo, body); } @@ -516,6 +554,17 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { .forEach(cookie -> this.headers.add(HttpHeaders.COOKIE, cookie.toString())); } } + + private URI getUrlToUse() { + MultiValueMap params = + this.queryParamsBuilder.buildAndExpand().encode().getQueryParams(); + + if (!params.isEmpty()) { + return UriComponentsBuilder.fromUri(this.url).queryParams(params).build(true).toUri(); + } + + return this.url; + } } } \ No newline at end of file