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 -> {