From 8edc68095751246503e8b7cd282a916ee6c29d0f Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 27 Jan 2017 15:59:03 -0500 Subject: [PATCH] Scale back static factory methods in ClientRequest Following on the introduction of WebClientOperations in the last commit this commit removes the HTTP method specific factory methods and also scales back the builder options in ClientRequest. ClientRequest is now expected to be used mainly from an ExchangeFilterFunction which may modify the ClientRequest using the from(ClientRequest) entry point rather creating from scratch. Issue: SPR-15124 --- .../function/client/ClientRequest.java | 177 ++---------------- .../client/DefaultClientRequestBuilder.java | 56 +----- .../client/DefaultWebClientOperations.java | 37 ++-- .../reactive/FlushingIntegrationTests.java | 29 +-- .../DefaultClientRequestBuilderTests.java | 113 ++--------- .../client/ExchangeFilterFunctionsTests.java | 10 +- .../client/WebClientIntegrationTests.java | 11 -- .../SseHandlerFunctionIntegrationTests.java | 62 +++--- .../annotation/SseIntegrationTests.java | 75 ++++---- 9 files changed, 142 insertions(+), 428 deletions(-) diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java index b0dd1186170..608d17084a7 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java @@ -17,27 +17,25 @@ package org.springframework.web.reactive.function.client; import java.net.URI; -import java.nio.charset.Charset; -import java.time.ZonedDateTime; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ClientHttpRequest; import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; -import org.springframework.web.util.DefaultUriTemplateHandler; -import org.springframework.web.util.UriTemplateHandler; /** - * Represents a typed, immutable, client-side HTTP request, as executed by the {@link WebClient}. - * Instances of this interface are created via static builder methods: - * {@link #method(HttpMethod, String, Object...)}, {@link #GET(String, Object...)}, etc. + * Represents a typed, immutable, client-side HTTP request, as executed by the + * {@link WebClient}. Instances of this interface can be created via static + * builder methods in this class. * + *

Note that applications are more likely to perform requests through + * {@link WebClientOperations} rather than using this directly. + * : * @param the type of the body that this request contains * @author Brian Clozel * @author Arjen Poutsma @@ -45,8 +43,6 @@ import org.springframework.web.util.UriTemplateHandler; */ public interface ClientRequest { - // Instance methods - /** * Return the HTTP method. */ @@ -81,6 +77,7 @@ public interface ClientRequest { */ Mono writeTo(ClientHttpRequest request, WebClientStrategies strategies); + // Static builder methods /** @@ -89,7 +86,7 @@ public interface ClientRequest { * @param other the request to copy the method, URI, headers, and cookies from * @return the created builder */ - static BodyBuilder 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()) @@ -102,100 +99,15 @@ public interface ClientRequest { * @param url the URL * @return the created builder */ - static BodyBuilder method(HttpMethod method, URI url) { - return new DefaultClientRequestBuilder(method, url); - } - - /** - * Create a builder with the given method and url template. - * @param method the HTTP method (GET, POST, etc) - * @param urlTemplate the URL template - * @param uriVariables optional variables to expand the template - * @return the created builder - */ - static BodyBuilder method(HttpMethod method, String urlTemplate, Object... uriVariables) { - UriTemplateHandler templateHandler = new DefaultUriTemplateHandler(); - URI url = templateHandler.expand(urlTemplate, uriVariables); + static Builder method(HttpMethod method, URI url) { return new DefaultClientRequestBuilder(method, url); } - /** - * Create an HTTP GET builder with the given url template. - * @param urlTemplate the URL template - * @param uriVariables optional variables to expand the template - * @return the created builder - */ - static HeadersBuilder GET(String urlTemplate, Object... uriVariables) { - return method(HttpMethod.GET, urlTemplate, uriVariables); - } - - /** - * Create an HTTP HEAD builder with the given url template. - * @param urlTemplate the URL template - * @param uriVariables optional variables to expand the template - * @return the created builder - */ - static HeadersBuilder HEAD(String urlTemplate, Object... uriVariables) { - return method(HttpMethod.HEAD, urlTemplate, uriVariables); - } - - /** - * Create an HTTP POST builder with the given url template. - * @param urlTemplate the URL template - * @param uriVariables optional variables to expand the template - * @return the created builder - */ - static BodyBuilder POST(String urlTemplate, Object... uriVariables) { - return method(HttpMethod.POST, urlTemplate, uriVariables); - } - - /** - * Create an HTTP PUT builder with the given url template. - * @param urlTemplate the URL template - * @param uriVariables optional variables to expand the template - * @return the created builder - */ - static BodyBuilder PUT(String urlTemplate, Object... uriVariables) { - return method(HttpMethod.PUT, urlTemplate, uriVariables); - } - - /** - * Create an HTTP PATCH builder with the given url template. - * @param urlTemplate the URL template - * @param uriVariables optional variables to expand the template - * @return the created builder - */ - static BodyBuilder PATCH(String urlTemplate, Object... uriVariables) { - return method(HttpMethod.PATCH, urlTemplate, uriVariables); - } - - /** - * Create an HTTP DELETE builder with the given url template. - * @param urlTemplate the URL template - * @param uriVariables optional variables to expand the template - * @return the created builder - */ - static HeadersBuilder DELETE(String urlTemplate, Object... uriVariables) { - return method(HttpMethod.DELETE, urlTemplate, uriVariables); - } - - /** - * Creates an HTTP OPTIONS builder with the given url template. - * @param urlTemplate the URL template - * @param uriVariables optional variables to expand the template - * @return the created builder - */ - static HeadersBuilder OPTIONS(String urlTemplate, Object... uriVariables) { - return method(HttpMethod.OPTIONS, urlTemplate, uriVariables); - } - /** - * Defines a builder that adds headers to the request. - * - * @param the builder subclass + * Defines a builder for a request. */ - interface HeadersBuilder> { + interface Builder { /** * Add the given, single header value under the given name. @@ -204,7 +116,7 @@ public interface ClientRequest { * @return this builder * @see HttpHeaders#add(String, String) */ - B header(String headerName, String... headerValues); + Builder header(String headerName, String... headerValues); /** * Copy the given headers into the entity's headers map. @@ -212,39 +124,7 @@ public interface ClientRequest { * @param headers the existing HttpHeaders to copy from * @return this builder */ - B headers(HttpHeaders headers); - - /** - * Set the list of acceptable {@linkplain MediaType media types}, as - * specified by the {@code Accept} header. - * @param acceptableMediaTypes the acceptable media types - * @return this builder - */ - B accept(MediaType... acceptableMediaTypes); - - /** - * Set the list of acceptable {@linkplain Charset charsets}, as specified - * by the {@code Accept-Charset} header. - * @param acceptableCharsets the acceptable charsets - * @return this builder - */ - B acceptCharset(Charset... acceptableCharsets); - - /** - * Set the value of the {@code If-Modified-Since} header. - *

The date should be specified as the number of milliseconds since - * January 1, 1970 GMT. - * @param ifModifiedSince the new value of the header - * @return this builder - */ - B ifModifiedSince(ZonedDateTime ifModifiedSince); - - /** - * Set the values of the {@code If-None-Match} header. - * @param ifNoneMatches the new value of the header - * @return this builder - */ - B ifNoneMatch(String... ifNoneMatches); + Builder headers(HttpHeaders headers); /** * Add a cookie with the given name and value. @@ -252,7 +132,7 @@ public interface ClientRequest { * @param value the cookie value * @return this builder */ - B cookie(String name, String value); + Builder cookie(String name, String value); /** * Copy the given cookies into the entity's cookies map. @@ -260,39 +140,13 @@ public interface ClientRequest { * @param cookies the existing cookies to copy from * @return this builder */ - B cookies(MultiValueMap cookies); + Builder cookies(MultiValueMap cookies); /** * Builds the request entity with no body. * @return the request entity */ ClientRequest build(); - } - - - /** - * Defines a builder that adds a body to the request entity. - */ - interface BodyBuilder extends HeadersBuilder { - - - /** - * Set the length of the body in bytes, as specified by the - * {@code Content-Length} header. - * @param contentLength the content length - * @return this builder - * @see HttpHeaders#setContentLength(long) - */ - BodyBuilder contentLength(long contentLength); - - /** - * Set the {@linkplain MediaType media type} of the body, as specified - * by the {@code Content-Type} header. - * @param contentType the content type - * @return this builder - * @see HttpHeaders#setContentType(MediaType) - */ - BodyBuilder contentType(MediaType contentType); /** * Set the body of the request to the given {@code BodyInserter} and return it. @@ -314,5 +168,4 @@ public interface ClientRequest { } - } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java index 0af5c1b2c20..e0f65d6db8d 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java @@ -17,11 +17,6 @@ package org.springframework.web.reactive.function.client; import java.net.URI; -import java.nio.charset.Charset; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.function.Supplier; @@ -33,7 +28,6 @@ import reactor.core.publisher.Mono; import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ClientHttpRequest; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.util.Assert; @@ -44,12 +38,12 @@ import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.BodyInserters; /** - * Default implementation of {@link ClientRequest.BodyBuilder}. + * Default implementation of {@link ClientRequest.Builder}. * * @author Arjen Poutsma * @since 5.0 */ -class DefaultClientRequestBuilder implements ClientRequest.BodyBuilder { +class DefaultClientRequestBuilder implements ClientRequest.Builder { private final HttpMethod method; @@ -66,7 +60,7 @@ class DefaultClientRequestBuilder implements ClientRequest.BodyBuilder { } @Override - public ClientRequest.BodyBuilder header(String headerName, String... headerValues) { + public ClientRequest.Builder header(String headerName, String... headerValues) { for (String headerValue : headerValues) { this.headers.add(headerName, headerValue); } @@ -74,7 +68,7 @@ class DefaultClientRequestBuilder implements ClientRequest.BodyBuilder { } @Override - public ClientRequest.BodyBuilder headers(HttpHeaders headers) { + public ClientRequest.Builder headers(HttpHeaders headers) { if (headers != null) { this.headers.putAll(headers); } @@ -82,39 +76,13 @@ class DefaultClientRequestBuilder implements ClientRequest.BodyBuilder { } @Override - public ClientRequest.BodyBuilder accept(MediaType... acceptableMediaTypes) { - this.headers.setAccept(Arrays.asList(acceptableMediaTypes)); - return this; - } - - @Override - public ClientRequest.BodyBuilder acceptCharset(Charset... acceptableCharsets) { - this.headers.setAcceptCharset(Arrays.asList(acceptableCharsets)); - return this; - } - - @Override - public ClientRequest.BodyBuilder ifModifiedSince(ZonedDateTime ifModifiedSince) { - ZonedDateTime gmt = ifModifiedSince.withZoneSameInstant(ZoneId.of("GMT")); - String headerValue = DateTimeFormatter.RFC_1123_DATE_TIME.format(gmt); - this.headers.set(HttpHeaders.IF_MODIFIED_SINCE, headerValue); - return this; - } - - @Override - public ClientRequest.BodyBuilder ifNoneMatch(String... ifNoneMatches) { - this.headers.setIfNoneMatch(Arrays.asList(ifNoneMatches)); - return this; - } - - @Override - public ClientRequest.BodyBuilder cookie(String name, String value) { + public ClientRequest.Builder cookie(String name, String value) { this.cookies.add(name, value); return this; } @Override - public ClientRequest.BodyBuilder cookies(MultiValueMap cookies) { + public ClientRequest.Builder cookies(MultiValueMap cookies) { if (cookies != null) { this.cookies.putAll(cookies); } @@ -126,18 +94,6 @@ class DefaultClientRequestBuilder implements ClientRequest.BodyBuilder { return body(BodyInserters.empty()); } - @Override - public ClientRequest.BodyBuilder contentLength(long contentLength) { - this.headers.setContentLength(contentLength); - return this; - } - - @Override - public ClientRequest.BodyBuilder contentType(MediaType contentType) { - this.headers.setContentType(contentType); - return this; - } - @Override public ClientRequest body(BodyInserter inserter) { Assert.notNull(inserter, "'inserter' must not be null"); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperations.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperations.java index bc2ea689f81..0c06efe9dc6 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperations.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperations.java @@ -17,7 +17,10 @@ package org.springframework.web.reactive.function.client; import java.net.URI; import java.nio.charset.Charset; +import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; import java.util.function.Function; import org.jetbrains.annotations.NotNull; @@ -137,47 +140,53 @@ class DefaultWebClientOperations implements WebClientOperations { private class DefaultHeaderSpec implements HeaderSpec { - private ClientRequest.BodyBuilder requestBuilder; + private final ClientRequest.Builder requestBuilder; + private final HttpHeaders headers = new HttpHeaders(); - DefaultHeaderSpec(ClientRequest.BodyBuilder requestBuilder) { + + DefaultHeaderSpec(ClientRequest.Builder requestBuilder) { this.requestBuilder = requestBuilder; } @Override public DefaultHeaderSpec header(String headerName, String... headerValues) { - this.requestBuilder.header(headerName, headerValues); + for (String headerValue : headerValues) { + this.headers.add(headerName, headerValue); + } return this; } @Override public DefaultHeaderSpec headers(HttpHeaders headers) { - this.requestBuilder.headers(headers); + if (headers != null) { + this.headers.putAll(headers); + } return this; } @Override public DefaultHeaderSpec accept(MediaType... acceptableMediaTypes) { - this.requestBuilder.accept(acceptableMediaTypes); + this.headers.setAccept(Arrays.asList(acceptableMediaTypes)); return this; } @Override public DefaultHeaderSpec acceptCharset(Charset... acceptableCharsets) { - this.requestBuilder.acceptCharset(acceptableCharsets); + this.headers.setAcceptCharset(Arrays.asList(acceptableCharsets)); return this; } @Override public DefaultHeaderSpec contentType(MediaType contentType) { - this.requestBuilder.contentType(contentType); + this.headers.setContentType(contentType); return this; } @Override public DefaultHeaderSpec contentLength(long contentLength) { - this.requestBuilder.contentLength(contentLength); + this.headers.setContentLength(contentLength); return this; } @@ -195,31 +204,33 @@ class DefaultWebClientOperations implements WebClientOperations { @Override public DefaultHeaderSpec ifModifiedSince(ZonedDateTime ifModifiedSince) { - this.requestBuilder.ifModifiedSince(ifModifiedSince); + ZonedDateTime gmt = ifModifiedSince.withZoneSameInstant(ZoneId.of("GMT")); + String headerValue = DateTimeFormatter.RFC_1123_DATE_TIME.format(gmt); + this.headers.set(HttpHeaders.IF_MODIFIED_SINCE, headerValue); return this; } @Override public DefaultHeaderSpec ifNoneMatch(String... ifNoneMatches) { - this.requestBuilder.ifNoneMatch(ifNoneMatches); + this.headers.setIfNoneMatch(Arrays.asList(ifNoneMatches)); return this; } @Override public Mono exchange() { - ClientRequest request = this.requestBuilder.build(); + ClientRequest request = this.requestBuilder.headers(this.headers).build(); return getWebClient().exchange(request); } @Override public Mono exchange(BodyInserter inserter) { - ClientRequest request = this.requestBuilder.body(inserter); + ClientRequest request = this.requestBuilder.headers(this.headers).body(inserter); return getWebClient().exchange(request); } @Override public > Mono exchange(S publisher, Class elementClass) { - ClientRequest request = this.requestBuilder.body(publisher, elementClass); + ClientRequest request = this.requestBuilder.headers(this.headers).body(publisher, elementClass); return getWebClient().exchange(request); } } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/FlushingIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/FlushingIntegrationTests.java index 7cd0d14e323..5e72bcea6d1 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/FlushingIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/FlushingIntegrationTests.java @@ -37,15 +37,17 @@ import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.bootstrap.RxNettyHttpServer; import org.springframework.util.Assert; import org.springframework.web.reactive.function.BodyExtractors; -import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientOperations; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.UriBuilderFactory; /** * @author Sebastien Deleuze */ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTests { - private WebClient webClient; + private WebClientOperations operations; @Before @@ -55,15 +57,18 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest Assume.assumeFalse(this.server instanceof RxNettyHttpServer); super.setup(); - this.webClient = WebClient.create(new ReactorClientHttpConnector()); + + WebClient client = WebClient.create(new ReactorClientHttpConnector()); + UriBuilderFactory factory = new DefaultUriBuilderFactory("http://localhost:" + this.port); + this.operations = WebClientOperations.builder(client).uriBuilderFactory(factory).build(); } @Test public void writeAndFlushWith() throws Exception { - ClientRequest request = ClientRequest.GET("http://localhost:" + port + "/write-and-flush").build(); - Mono result = this.webClient - .exchange(request) + Mono result = this.operations.get() + .uri("/write-and-flush") + .exchange() .flatMap(response -> response.body(BodyExtractors.toFlux(String.class))) .takeUntil(s -> s.endsWith("data1")) .reduce((s1, s2) -> s1 + s2); @@ -76,9 +81,9 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest @Test // SPR-14991 public void writeAndAutoFlushOnComplete() { - ClientRequest request = ClientRequest.GET("http://localhost:" + port + "/write-and-complete").build(); - Mono result = this.webClient - .exchange(request) + Mono result = this.operations.get() + .uri("/write-and-complete") + .exchange() .flatMap(response -> response.bodyToFlux(String.class)) .reduce((s1, s2) -> s1 + s2); @@ -90,9 +95,9 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest @Test // SPR-14992 public void writeAndAutoFlushBeforeComplete() { - ClientRequest request = ClientRequest.GET("http://localhost:" + port + "/write-and-never-complete").build(); - Flux result = this.webClient - .exchange(request) + Flux result = this.operations.get() + .uri("/write-and-never-complete") + .exchange() .flatMap(response -> response.bodyToFlux(String.class)); StepVerifier.create(result) diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java index a32dbd53918..b16ad3c0c7e 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java @@ -18,11 +18,7 @@ package org.springframework.web.reactive.function.client; import java.net.URI; import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.junit.Test; @@ -31,8 +27,6 @@ import reactor.core.publisher.Mono; import org.springframework.core.codec.CharSequenceEncoder; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ClientHttpRequest; import org.springframework.http.codec.EncoderHttpMessageWriter; import org.springframework.http.codec.HttpMessageWriter; @@ -45,6 +39,9 @@ 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; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.POST; /** * @author Arjen Poutsma @@ -53,12 +50,12 @@ public class DefaultClientRequestBuilderTests { @Test public void from() throws Exception { - ClientRequest other = ClientRequest.GET("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(); assertEquals(new URI("http://example.com"), result.url()); - assertEquals(HttpMethod.GET, result.method()); + assertEquals(GET, result.method()); assertEquals("bar", result.headers().getFirst("foo")); assertEquals("qux", result.cookies().getFirst("baz")); } @@ -66,112 +63,26 @@ public class DefaultClientRequestBuilderTests { @Test public void method() throws Exception { URI url = new URI("http://example.com"); - ClientRequest result = ClientRequest.method(HttpMethod.DELETE, url).build(); + ClientRequest result = ClientRequest.method(DELETE, url).build(); assertEquals(url, result.url()); - assertEquals(HttpMethod.DELETE, result.method()); - } - - @Test - public void GET() throws Exception { - URI url = new URI("http://example.com"); - ClientRequest result = ClientRequest.GET(url.toString()).build(); - assertEquals(url, result.url()); - assertEquals(HttpMethod.GET, result.method()); - } - - @Test - public void HEAD() throws Exception { - URI url = new URI("http://example.com"); - ClientRequest result = ClientRequest.HEAD(url.toString()).build(); - assertEquals(url, result.url()); - assertEquals(HttpMethod.HEAD, result.method()); - } - - @Test - public void POST() throws Exception { - URI url = new URI("http://example.com"); - ClientRequest result = ClientRequest.POST(url.toString()).build(); - assertEquals(url, result.url()); - assertEquals(HttpMethod.POST, result.method()); - } - - @Test - public void PUT() throws Exception { - URI url = new URI("http://example.com"); - ClientRequest result = ClientRequest.PUT(url.toString()).build(); - assertEquals(url, result.url()); - assertEquals(HttpMethod.PUT, result.method()); - } - - @Test - public void PATCH() throws Exception { - URI url = new URI("http://example.com"); - ClientRequest result = ClientRequest.PATCH(url.toString()).build(); - assertEquals(url, result.url()); - assertEquals(HttpMethod.PATCH, result.method()); - } - - @Test - public void DELETE() throws Exception { - URI url = new URI("http://example.com"); - ClientRequest result = ClientRequest.DELETE(url.toString()).build(); - assertEquals(url, result.url()); - assertEquals(HttpMethod.DELETE, result.method()); - } - - @Test - public void OPTIONS() throws Exception { - URI url = new URI("http://example.com"); - ClientRequest result = ClientRequest.OPTIONS(url.toString()).build(); - assertEquals(url, result.url()); - assertEquals(HttpMethod.OPTIONS, result.method()); - } - - @Test - public void accept() throws Exception { - MediaType json = MediaType.APPLICATION_JSON; - ClientRequest result = ClientRequest.GET("http://example.com").accept(json).build(); - assertEquals(Collections.singletonList(json), result.headers().getAccept()); - } - - @Test - public void acceptCharset() throws Exception { - Charset charset = Charset.defaultCharset(); - ClientRequest result = ClientRequest.GET("http://example.com") - .acceptCharset(charset).build(); - assertEquals(Collections.singletonList(charset), result.headers().getAcceptCharset()); - } - - @Test - public void ifModifiedSince() throws Exception { - ZonedDateTime now = ZonedDateTime.now(); - ClientRequest result = ClientRequest.GET("http://example.com") - .ifModifiedSince(now).build(); - assertEquals(now.toInstant().toEpochMilli()/1000, result.headers().getIfModifiedSince()/1000); - } - - @Test - public void ifNoneMatch() throws Exception { - ClientRequest result = ClientRequest.GET("http://example.com") - .ifNoneMatch("\"v2.7\"", "\"v2.8\"").build(); - assertEquals(Arrays.asList("\"v2.7\"", "\"v2.8\""), result.headers().getIfNoneMatch()); + assertEquals(DELETE, result.method()); } @Test public void cookie() throws Exception { - ClientRequest result = ClientRequest.GET("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.GET("http://example.com") + ClientRequest result = ClientRequest.method(GET, URI.create("http://example.com")) .header("MyKey", "MyValue") .cookie("foo", "bar") .build(); - MockClientHttpRequest request = new MockClientHttpRequest(HttpMethod.GET, "/"); + MockClientHttpRequest request = new MockClientHttpRequest(GET, "/"); WebClientStrategies strategies = mock(WebClientStrategies.class); result.writeTo(request, strategies).block(); @@ -193,7 +104,7 @@ public class DefaultClientRequestBuilderTests { return response.writeWith(Mono.just(buffer)); }; - ClientRequest result = ClientRequest.POST("http://example.com") + ClientRequest result = ClientRequest.method(POST, URI.create("http://example.com")) .body(inserter); List> messageWriters = new ArrayList<>(); @@ -202,7 +113,7 @@ public class DefaultClientRequestBuilderTests { WebClientStrategies strategies = mock(WebClientStrategies.class); when(strategies.messageWriters()).thenReturn(messageWriters::stream); - MockClientHttpRequest request = new MockClientHttpRequest(HttpMethod.GET, "/"); + MockClientHttpRequest request = new MockClientHttpRequest(GET, "/"); result.writeTo(request, strategies).block(); assertNotNull(request.getBody()); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java index 299ce98b22b..37bcc3e4c8f 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java @@ -16,15 +16,19 @@ package org.springframework.web.reactive.function.client; +import java.net.URI; + 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; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.springframework.http.HttpMethod.GET; /** * @author Arjen Poutsma @@ -33,7 +37,7 @@ public class ExchangeFilterFunctionsTests { @Test public void andThen() throws Exception { - ClientRequest request = ClientRequest.GET("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); @@ -63,7 +67,7 @@ public class ExchangeFilterFunctionsTests { @Test public void apply() throws Exception { - ClientRequest request = ClientRequest.GET("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); @@ -82,7 +86,7 @@ public class ExchangeFilterFunctionsTests { @Test public void basicAuthentication() throws Exception { - ClientRequest request = ClientRequest.GET("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-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java index 9d8a2c0b924..5dabfc061cd 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java @@ -18,7 +18,6 @@ package org.springframework.web.reactive.function.client; import java.time.Duration; -import okhttp3.HttpUrl; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; @@ -36,15 +35,11 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.http.codec.Pojo; -import org.springframework.web.reactive.function.BodyExtractors; -import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.UriBuilderFactory; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; -import static org.springframework.web.reactive.function.BodyExtractors.toFlux; -import static org.springframework.web.reactive.function.BodyExtractors.toMono; import static org.springframework.web.reactive.function.BodyInserters.fromObject; /** @@ -171,14 +166,9 @@ public class WebClientIntegrationTests { @Test public void jsonPojoFlux() throws Exception { - HttpUrl baseUrl = server.url("/pojos"); this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json") .setBody("[{\"bar\":\"bar1\",\"foo\":\"foo1\"},{\"bar\":\"bar2\",\"foo\":\"foo2\"}]")); - ClientRequest request = ClientRequest.GET(baseUrl.toString()) - .accept(MediaType.APPLICATION_JSON) - .build(); - Flux result = this.operations.get() .uri("/pojos") .accept(MediaType.APPLICATION_JSON) @@ -199,7 +189,6 @@ public class WebClientIntegrationTests { @Test public void postJsonPojo() throws Exception { - HttpUrl baseUrl = server.url("/pojo/capitalize"); this.server.enqueue(new MockResponse() .setHeader("Content-Type", "application/json") .setBody("{\"bar\":\"BARBAR\",\"foo\":\"FOOFOO\"}")); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/server/SseHandlerFunctionIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/server/SseHandlerFunctionIntegrationTests.java index 23618990b5c..e58fc194310 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/server/SseHandlerFunctionIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/server/SseHandlerFunctionIntegrationTests.java @@ -18,22 +18,24 @@ package org.springframework.web.reactive.function.server; import java.time.Duration; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import org.junit.Before; import org.junit.Test; -import static org.springframework.http.MediaType.TEXT_EVENT_STREAM; -import static org.springframework.web.reactive.function.BodyExtractors.toFlux; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import org.springframework.core.ResolvableType; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.http.codec.ServerSentEvent; -import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientOperations; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.UriBuilderFactory; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.springframework.core.ResolvableType.forClassWithGenerics; +import static org.springframework.http.MediaType.TEXT_EVENT_STREAM; +import static org.springframework.web.reactive.function.BodyExtractors.toFlux; import static org.springframework.web.reactive.function.BodyInserters.fromServerSentEvents; import static org.springframework.web.reactive.function.server.RouterFunctions.route; @@ -42,12 +44,15 @@ import static org.springframework.web.reactive.function.server.RouterFunctions.r */ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIntegrationTests { + private WebClientOperations operations; - private WebClient webClient; @Before - public void createWebClient() { - this.webClient = WebClient.create(new ReactorClientHttpConnector()); + public void setup() throws Exception { + super.setup(); + WebClient client = WebClient.create(new ReactorClientHttpConnector()); + UriBuilderFactory factory = new DefaultUriBuilderFactory("http://localhost:" + this.port); + this.operations = WebClientOperations.builder(client).uriBuilderFactory(factory).build(); } @Override @@ -60,13 +65,10 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn @Test public void sseAsString() throws Exception { - ClientRequest request = ClientRequest - .GET("http://localhost:{port}/string", this.port) - .accept(TEXT_EVENT_STREAM) - .build(); - - Flux result = this.webClient - .exchange(request) + Flux result = this.operations.get() + .uri("/string") + .accept(TEXT_EVENT_STREAM) + .exchange() .flatMap(response -> response.body(toFlux(String.class))); StepVerifier.create(result) @@ -77,14 +79,10 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn } @Test public void sseAsPerson() throws Exception { - ClientRequest request = - ClientRequest - .GET("http://localhost:{port}/person", this.port) - .accept(TEXT_EVENT_STREAM) - .build(); - - Flux result = this.webClient - .exchange(request) + Flux result = this.operations.get() + .uri("/person") + .accept(TEXT_EVENT_STREAM) + .exchange() .flatMap(response -> response.body(toFlux(Person.class))); StepVerifier.create(result) @@ -96,16 +94,12 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn @Test public void sseAsEvent() throws Exception { - ClientRequest request = - ClientRequest - .GET("http://localhost:{port}/event", this.port) - .accept(TEXT_EVENT_STREAM) - .build(); - - ResolvableType type = ResolvableType.forClassWithGenerics(ServerSentEvent.class, String.class); - Flux> result = this.webClient - .exchange(request) - .flatMap(response -> response.body(toFlux(type))); + Flux> result = this.operations.get() + .uri("/event") + .accept(TEXT_EVENT_STREAM) + .exchange() + .flatMap(response -> response.body(toFlux( + forClassWithGenerics(ServerSentEvent.class, String.class)))); StepVerifier.create(result) .consumeNextWith( event -> { diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java index 676f30c5648..83387cc0a25 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java @@ -18,14 +18,9 @@ package org.springframework.web.reactive.result.method.annotation; import java.time.Duration; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import org.junit.Before; import org.junit.Test; -import static org.springframework.http.MediaType.TEXT_EVENT_STREAM; -import static org.springframework.web.reactive.function.BodyExtractors.toFlux; import reactor.core.publisher.Flux; - import reactor.test.StepVerifier; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -40,9 +35,17 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.config.EnableWebReactive; -import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientOperations; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; +import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.UriBuilderFactory; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.springframework.core.ResolvableType.forClassWithGenerics; +import static org.springframework.http.MediaType.TEXT_EVENT_STREAM; +import static org.springframework.web.reactive.function.BodyExtractors.toFlux; /** @@ -52,14 +55,16 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { private AnnotationConfigApplicationContext wac; - private WebClient webClient; + private WebClientOperations operations; @Override @Before public void setup() throws Exception { super.setup(); - this.webClient = WebClient.create(new ReactorClientHttpConnector()); + WebClient client = WebClient.create(new ReactorClientHttpConnector()); + UriBuilderFactory factory = new DefaultUriBuilderFactory("http://localhost:" + this.port + "/sse"); + this.operations = WebClientOperations.builder(client).uriBuilderFactory(factory).build(); } @@ -74,14 +79,11 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { @Test public void sseAsString() throws Exception { - ClientRequest request = ClientRequest - .GET("http://localhost:{port}/sse/string", this.port) - .accept(TEXT_EVENT_STREAM) - .build(); - - Flux result = this.webClient - .exchange(request) - .flatMap(response -> response.body(toFlux(String.class))); + Flux result = this.operations.get() + .uri("/string") + .accept(TEXT_EVENT_STREAM) + .exchange() + .flatMap(response -> response.bodyToFlux(String.class)); StepVerifier.create(result) .expectNext("foo 0") @@ -91,15 +93,11 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { } @Test public void sseAsPerson() throws Exception { - ClientRequest request = - ClientRequest - .GET("http://localhost:{port}/sse/person", this.port) - .accept(TEXT_EVENT_STREAM) - .build(); - - Flux result = this.webClient - .exchange(request) - .flatMap(response -> response.body(toFlux(Person.class))); + Flux result = this.operations.get() + .uri("/person") + .accept(TEXT_EVENT_STREAM) + .exchange() + .flatMap(response -> response.bodyToFlux(Person.class)); StepVerifier.create(result) .expectNext(new Person("foo 0")) @@ -110,15 +108,11 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { @Test public void sseAsEvent() throws Exception { - ClientRequest request = - ClientRequest - .GET("http://localhost:{port}/sse/event", this.port) - .accept(TEXT_EVENT_STREAM) - .build(); - - ResolvableType type = ResolvableType.forClassWithGenerics(ServerSentEvent.class, String.class); - Flux> result = this.webClient - .exchange(request) + ResolvableType type = forClassWithGenerics(ServerSentEvent.class, String.class); + Flux> result = this.operations.get() + .uri("/event") + .accept(TEXT_EVENT_STREAM) + .exchange() .flatMap(response -> response.body(toFlux(type))); StepVerifier.create(result) @@ -142,15 +136,12 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { @Test public void sseAsEventWithoutAcceptHeader() throws Exception { - ClientRequest request = - ClientRequest - .GET("http://localhost:{port}/sse/event", this.port) + Flux> result = this.operations.get() + .uri("/event") .accept(TEXT_EVENT_STREAM) - .build(); - - Flux> result = this.webClient - .exchange(request) - .flatMap(response -> response.body(toFlux(ResolvableType.forClassWithGenerics(ServerSentEvent.class, String.class)))); + .exchange() + .flatMap(response -> response.body(toFlux( + forClassWithGenerics(ServerSentEvent.class, String.class)))); StepVerifier.create(result) .consumeNextWith( event -> {