From 45770d73ede714533741f97275996ea7a04a381a Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 10 Feb 2017 11:46:38 +0100 Subject: [PATCH] Remove parameterisation from ClientRequest This commit removes the parameterisation from ClientRequest, similarly to ServerResponse. Dropping the parameterisation facilitates a ClientRequest.from method that also copies the body of the target request. SPR-15234 Work in Progress --- .../server/WiretapConnectorTests.java | 3 +- .../function/client/ClientRequest.java | 35 +++++++++---------- .../client/DefaultClientRequestBuilder.java | 31 +++++++++------- .../function/client/DefaultWebClient.java | 6 ++-- .../client/ExchangeFilterFunction.java | 8 ++--- .../client/ExchangeFilterFunctions.java | 6 ++-- .../function/client/ExchangeFunction.java | 2 +- .../function/client/ExchangeFunctions.java | 2 +- .../DefaultClientRequestBuilderTests.java | 15 ++++---- .../client/DefaultWebClientTests.java | 19 +++++----- .../client/ExchangeFilterFunctionsTests.java | 9 +++-- .../client/WebClientIntegrationTests.java | 4 +-- 12 files changed, 73 insertions(+), 67 deletions(-) diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/WiretapConnectorTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/WiretapConnectorTests.java index 7468c66d731..622379ec44d 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/WiretapConnectorTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/WiretapConnectorTests.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.test.web.reactive.server; import java.net.URI; @@ -54,7 +55,7 @@ public class WiretapConnectorTests { wiretapConnector.addListener(infoRef::set); ExchangeFunction exchangeFn = ExchangeFunctions.create(wiretapConnector); - ClientRequest clientRequest = ClientRequest.method(HttpMethod.GET, URI.create("/test")).build(); + ClientRequest clientRequest = ClientRequest.method(HttpMethod.GET, URI.create("/test")).build(); exchangeFn.exchange(clientRequest).blockMillis(0); WiretapConnector.Info info = infoRef.get(); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java index a36077f248b..bcdfb562d1b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java @@ -36,12 +36,11 @@ import org.springframework.web.reactive.function.BodyInserter; *

Note that applications are more likely to perform requests through * {@link WebClient} rather than using this directly. * - * @param the type of the body that this request contains * @author Brian Clozel * @author Arjen Poutsma * @since 5.0 */ -public interface ClientRequest { +public interface ClientRequest { /** * Return the HTTP method. @@ -66,7 +65,7 @@ public interface ClientRequest { /** * Return the body inserter of this request. */ - BodyInserter inserter(); + BodyInserter body(); /** * Writes this request to the given {@link ClientHttpRequest}. @@ -86,11 +85,12 @@ public interface ClientRequest { * @param other the request to copy the method, URI, headers, and cookies from * @return the created builder */ - static Builder from(ClientRequest other) { + static Builder from(ClientRequest other) { Assert.notNull(other, "'other' must not be null"); return new DefaultClientRequestBuilder(other.method(), other.url()) .headers(other.headers()) - .cookies(other.cookies()); + .cookies(other.cookies()) + .body(other.body()); } /** @@ -143,28 +143,27 @@ public interface ClientRequest { Builder cookies(MultiValueMap cookies); /** - * Builds the request entity with no body. - * @return the request entity - */ - ClientRequest build(); - - /** - * Set the body of the request to the given {@code BodyInserter} and return it. + * Set the body of the request to the given {@code BodyInserter}. * @param inserter the {@code BodyInserter} that writes to the request - * @param the type contained in the body - * @return the built request + * @return this builder */ - ClientRequest body(BodyInserter inserter); + Builder body(BodyInserter inserter); /** * Set the body of the request to the given {@code Publisher} and return it. * @param publisher the {@code Publisher} to write to the request * @param elementClass the class of elements contained in the publisher - * @param the type of the elements contained in the publisher - * @param the type of the {@code Publisher} + * @param the type of the elements contained in the publisher + * @param

the type of the {@code Publisher} * @return the built request */ - > ClientRequest body(S publisher, Class elementClass); + > Builder body(P publisher, Class elementClass); + + /** + * Builds the request entity with no body. + * @return the request entity + */ + ClientRequest build(); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java index dadceae1c5d..09ecd793417 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java @@ -53,6 +53,8 @@ class DefaultClientRequestBuilder implements ClientRequest.Builder { private final MultiValueMap cookies = new LinkedMultiValueMap<>(); + private BodyInserter inserter = BodyInserters.empty(); + public DefaultClientRequestBuilder(HttpMethod method, URI url) { this.method = method; @@ -90,23 +92,28 @@ class DefaultClientRequestBuilder implements ClientRequest.Builder { } @Override - public ClientRequest build() { - return body(BodyInserters.empty()); + public > ClientRequest.Builder body(P publisher, + Class elementClass) { + Assert.notNull(publisher, "'publisher' must not be null"); + Assert.notNull(elementClass, "'elementClass' must not be null"); + + this.inserter = BodyInserters.fromPublisher(publisher, elementClass); + return this; } @Override - public ClientRequest body(BodyInserter inserter) { - Assert.notNull(inserter, "'inserter' must not be null"); - return new BodyInserterRequest(this.method, this.url, this.headers, this.cookies, - inserter); + public ClientRequest.Builder body(BodyInserter inserter) { + this.inserter = inserter != null ? inserter : BodyInserters.empty(); + return this; } @Override - public > ClientRequest body(S publisher, Class elementClass) { - return body(BodyInserters.fromPublisher(publisher, elementClass)); + public ClientRequest build() { + return new BodyInserterRequest(this.method, this.url, this.headers, this.cookies, + this.inserter); } - private static class BodyInserterRequest implements ClientRequest { + private static class BodyInserterRequest implements ClientRequest { private final HttpMethod method; @@ -116,11 +123,11 @@ class DefaultClientRequestBuilder implements ClientRequest.Builder { private final MultiValueMap cookies; - private final BodyInserter inserter; + private final BodyInserter inserter; public BodyInserterRequest(HttpMethod method, URI url, HttpHeaders headers, MultiValueMap cookies, - BodyInserter inserter) { + BodyInserter inserter) { this.method = method; this.url = url; this.headers = HttpHeaders.readOnlyHttpHeaders(headers); @@ -149,7 +156,7 @@ class DefaultClientRequestBuilder implements ClientRequest.Builder { } @Override - public BodyInserter inserter() { + public BodyInserter body() { return this.inserter; } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java index 24dca9f29d5..d4ce012edd5 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java @@ -262,19 +262,19 @@ class DefaultWebClient implements WebClient { @Override public Mono exchange() { - ClientRequest request = initRequestBuilder().build(); + ClientRequest request = this.initRequestBuilder().build(); return getExchangeFunction().exchange(request); } @Override public Mono exchange(BodyInserter inserter) { - ClientRequest request = initRequestBuilder().body(inserter); + ClientRequest request = this.initRequestBuilder().body(inserter).build(); return getExchangeFunction().exchange(request); } @Override public > Mono exchange(S publisher, Class elementClass) { - ClientRequest request = initRequestBuilder().headers(this.headers).body(publisher, elementClass); + ClientRequest request = initRequestBuilder().headers(this.headers).body(publisher, elementClass).build(); return getExchangeFunction().exchange(request); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunction.java index d0df689b167..7d597af7d0f 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunction.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -41,7 +41,7 @@ public interface ExchangeFilterFunction { * @param next the next exchange function in the chain * @return the filtered response */ - Mono filter(ClientRequest request, ExchangeFunction next); + Mono filter(ClientRequest request, ExchangeFunction next); /** * Return a composed filter function that first applies this filter, and then applies the @@ -74,8 +74,8 @@ public interface ExchangeFilterFunction { * @param requestProcessor the request processor * @return the filter adaptation of the request processor */ - static ExchangeFilterFunction ofRequestProcessor(Function, - Mono>> requestProcessor) { + static ExchangeFilterFunction ofRequestProcessor(Function> requestProcessor) { Assert.notNull(requestProcessor, "'requestProcessor' must not be null"); return (request, next) -> requestProcessor.apply(request).then(next::exchange); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctions.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctions.java index 382692c6832..cb890570306 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctions.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctions.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -47,9 +47,9 @@ public abstract class ExchangeFilterFunctions { return ExchangeFilterFunction.ofRequestProcessor( clientRequest -> { String authorization = authorization(username, password); - ClientRequest authorizedRequest = ClientRequest.from(clientRequest) + ClientRequest authorizedRequest = ClientRequest.from(clientRequest) .header(HttpHeaders.AUTHORIZATION, authorization) - .body(clientRequest.inserter()); + .build(); return Mono.just(authorizedRequest); }); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction.java index 7e184836408..687b7e49607 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction.java @@ -45,7 +45,7 @@ public interface ExchangeFunction { * @param request the request to exchange * @return the delayed response */ - Mono exchange(ClientRequest request); + Mono exchange(ClientRequest request); /** * Filters this exchange function with the given {@code ExchangeFilterFunction}, resulting in a diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java index f2dd3dcb943..e8306b30b9d 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java @@ -72,7 +72,7 @@ public abstract class ExchangeFunctions { } @Override - public Mono exchange(ClientRequest request) { + public Mono exchange(ClientRequest request) { Assert.notNull(request, "'request' must not be null"); return this.connector diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java index 9a5ff6aa879..3f9b440cf65 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java @@ -37,7 +37,6 @@ import org.springframework.web.reactive.function.BodyInserter; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.http.HttpMethod.DELETE; @@ -51,10 +50,10 @@ public class DefaultClientRequestBuilderTests { @Test public void from() throws Exception { - ClientRequest other = ClientRequest.method(GET, URI.create("http://example.com")) + ClientRequest other = ClientRequest.method(GET, URI.create("http://example.com")) .header("foo", "bar") .cookie("baz", "qux").build(); - ClientRequest result = ClientRequest.from(other).build(); + ClientRequest result = ClientRequest.from(other).build(); assertEquals(new URI("http://example.com"), result.url()); assertEquals(GET, result.method()); assertEquals("bar", result.headers().getFirst("foo")); @@ -64,21 +63,21 @@ public class DefaultClientRequestBuilderTests { @Test public void method() throws Exception { URI url = new URI("http://example.com"); - ClientRequest result = ClientRequest.method(DELETE, url).build(); + ClientRequest result = ClientRequest.method(DELETE, url).build(); assertEquals(url, result.url()); assertEquals(DELETE, result.method()); } @Test public void cookie() throws Exception { - ClientRequest result = ClientRequest.method(GET, URI.create("http://example.com")) + ClientRequest result = ClientRequest.method(GET, URI.create("http://example.com")) .cookie("foo", "bar").build(); assertEquals("bar", result.cookies().getFirst("foo")); } @Test public void build() throws Exception { - ClientRequest result = ClientRequest.method(GET, URI.create("http://example.com")) + ClientRequest result = ClientRequest.method(GET, URI.create("http://example.com")) .header("MyKey", "MyValue") .cookie("foo", "bar") .build(); @@ -105,8 +104,8 @@ public class DefaultClientRequestBuilderTests { return response.writeWith(Mono.just(buffer)); }; - ClientRequest result = ClientRequest.method(POST, URI.create("http://example.com")) - .body(inserter); + ClientRequest result = ClientRequest.method(POST, URI.create("http://example.com")) + .body(inserter).build(); List> messageWriters = new ArrayList<>(); messageWriters.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java index 408d1bae989..4cbf6e11fcf 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.reactive.function.client; import java.util.Collections; @@ -42,7 +43,7 @@ public class DefaultWebClientTests { private ExchangeFunction exchangeFunction; @Captor - private ArgumentCaptor> captor; + private ArgumentCaptor captor; @Before @@ -58,7 +59,7 @@ public class DefaultWebClientTests { WebClient client = builder().build(); client.get().uri("/path").exchange(); - ClientRequest request = verifyExchange(); + ClientRequest request = verifyExchange(); assertEquals("/base/path", request.url().toString()); assertEquals(new HttpHeaders(), request.headers()); assertEquals(Collections.emptyMap(), request.cookies()); @@ -69,7 +70,7 @@ public class DefaultWebClientTests { WebClient client = builder().build(); client.get().uri(builder -> builder.path("/path").queryParam("q", "12").build()).exchange(); - ClientRequest request = verifyExchange(); + ClientRequest request = verifyExchange(); assertEquals("/base/path?q=12", request.url().toString()); verifyNoMoreInteractions(this.exchangeFunction); } @@ -79,7 +80,7 @@ public class DefaultWebClientTests { WebClient client = builder().build(); client.get().uri(builder -> builder.replacePath("/path").build()).exchange(); - ClientRequest request = verifyExchange(); + ClientRequest request = verifyExchange(); assertEquals("/path", request.url().toString()); verifyNoMoreInteractions(this.exchangeFunction); } @@ -89,7 +90,7 @@ public class DefaultWebClientTests { WebClient client = builder().build(); client.get().uri("/path").accept(MediaType.APPLICATION_JSON).cookie("id", "123").exchange(); - ClientRequest request = verifyExchange(); + ClientRequest request = verifyExchange(); assertEquals("application/json", request.headers().getFirst("Accept")); assertEquals("123", request.cookies().getFirst("id")); verifyNoMoreInteractions(this.exchangeFunction); @@ -100,7 +101,7 @@ public class DefaultWebClientTests { WebClient client = builder().defaultHeader("Accept", "application/json").defaultCookie("id", "123").build(); client.get().uri("/path").exchange(); - ClientRequest request = verifyExchange(); + ClientRequest request = verifyExchange(); assertEquals("application/json", request.headers().getFirst("Accept")); assertEquals("123", request.cookies().getFirst("id")); verifyNoMoreInteractions(this.exchangeFunction); @@ -111,7 +112,7 @@ public class DefaultWebClientTests { WebClient client = builder().defaultHeader("Accept", "application/json").defaultCookie("id", "123").build(); client.get().uri("/path").header("Accept", "application/xml").cookie("id", "456").exchange(); - ClientRequest request = verifyExchange(); + ClientRequest request = verifyExchange(); assertEquals("application/xml", request.headers().getFirst("Accept")); assertEquals("456", request.cookies().getFirst("id")); verifyNoMoreInteractions(this.exchangeFunction); @@ -122,8 +123,8 @@ public class DefaultWebClientTests { return WebClient.builder().baseUrl("/base").exchangeFunction(this.exchangeFunction); } - private ClientRequest verifyExchange() { - ClientRequest request = this.captor.getValue(); + private ClientRequest verifyExchange() { + ClientRequest request = this.captor.getValue(); Mockito.verify(this.exchangeFunction).exchange(request); verifyNoMoreInteractions(this.exchangeFunction); return request; diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java index 37bcc3e4c8f..1ce638433d4 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -22,7 +22,6 @@ import org.junit.Test; import reactor.core.publisher.Mono; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -37,7 +36,7 @@ public class ExchangeFilterFunctionsTests { @Test public void andThen() throws Exception { - ClientRequest request = ClientRequest.method(GET, URI.create("http://example.com")).build(); + ClientRequest request = ClientRequest.method(GET, URI.create("http://example.com")).build(); ClientResponse response = mock(ClientResponse.class); ExchangeFunction exchange = r -> Mono.just(response); @@ -67,7 +66,7 @@ public class ExchangeFilterFunctionsTests { @Test public void apply() throws Exception { - ClientRequest request = ClientRequest.method(GET, URI.create("http://example.com")).build(); + ClientRequest request = ClientRequest.method(GET, URI.create("http://example.com")).build(); ClientResponse response = mock(ClientResponse.class); ExchangeFunction exchange = r -> Mono.just(response); @@ -86,7 +85,7 @@ public class ExchangeFilterFunctionsTests { @Test public void basicAuthentication() throws Exception { - ClientRequest request = ClientRequest.method(GET, URI.create("http://example.com")).build(); + ClientRequest request = ClientRequest.method(GET, URI.create("http://example.com")).build(); ClientResponse response = mock(ClientResponse.class); ExchangeFunction exchange = r -> { diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java index 254a0bd7622..3bc00550132 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java @@ -252,7 +252,7 @@ public class WebClientIntegrationTests { WebClient filteredClient = this.webClient.filter( (request, next) -> { - ClientRequest filteredRequest = ClientRequest.from(request).header("foo", "bar").build(); + ClientRequest filteredRequest = ClientRequest.from(request).header("foo", "bar").build(); return next.exchange(filteredRequest); }); @@ -278,7 +278,7 @@ public class WebClientIntegrationTests { WebClient filteredClient = this.webClient.filter( (request, next) -> { - ClientRequest filteredRequest = ClientRequest.from(request).header("foo", "bar").build(); + ClientRequest filteredRequest = ClientRequest.from(request).header("foo", "bar").build(); return next.exchange(filteredRequest); });