Browse Source

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
pull/1308/head
Rossen Stoyanchev 9 years ago
parent
commit
8edc680957
  1. 177
      spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java
  2. 56
      spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java
  3. 37
      spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperations.java
  4. 29
      spring-web-reactive/src/test/java/org/springframework/web/reactive/FlushingIntegrationTests.java
  5. 113
      spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java
  6. 10
      spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java
  7. 11
      spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java
  8. 62
      spring-web-reactive/src/test/java/org/springframework/web/reactive/function/server/SseHandlerFunctionIntegrationTests.java
  9. 75
      spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java

177
spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java

@ -17,27 +17,25 @@ @@ -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.
*
* <p>Note that applications are more likely to perform requests through
* {@link WebClientOperations} rather than using this directly.
* :
* @param <T> 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; @@ -45,8 +43,6 @@ import org.springframework.web.util.UriTemplateHandler;
*/
public interface ClientRequest<T> {
// Instance methods
/**
* Return the HTTP method.
*/
@ -81,6 +77,7 @@ public interface ClientRequest<T> { @@ -81,6 +77,7 @@ public interface ClientRequest<T> {
*/
Mono<Void> writeTo(ClientHttpRequest request, WebClientStrategies strategies);
// Static builder methods
/**
@ -89,7 +86,7 @@ public interface ClientRequest<T> { @@ -89,7 +86,7 @@ public interface ClientRequest<T> {
* @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<T> { @@ -102,100 +99,15 @@ public interface ClientRequest<T> {
* @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 <B> the builder subclass
* Defines a builder for a request.
*/
interface HeadersBuilder<B extends HeadersBuilder<B>> {
interface Builder {
/**
* Add the given, single header value under the given name.
@ -204,7 +116,7 @@ public interface ClientRequest<T> { @@ -204,7 +116,7 @@ public interface ClientRequest<T> {
* @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<T> { @@ -212,39 +124,7 @@ public interface ClientRequest<T> {
* @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.
* <p>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<T> { @@ -252,7 +132,7 @@ public interface ClientRequest<T> {
* @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<T> { @@ -260,39 +140,13 @@ public interface ClientRequest<T> {
* @param cookies the existing cookies to copy from
* @return this builder
*/
B cookies(MultiValueMap<String, String> cookies);
Builder cookies(MultiValueMap<String, String> cookies);
/**
* Builds the request entity with no body.
* @return the request entity
*/
ClientRequest<Void> build();
}
/**
* Defines a builder that adds a body to the request entity.
*/
interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
/**
* 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<T> { @@ -314,5 +168,4 @@ public interface ClientRequest<T> {
}
}

56
spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java

@ -17,11 +17,6 @@ @@ -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; @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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<String, String> cookies) {
public ClientRequest.Builder cookies(MultiValueMap<String, String> cookies) {
if (cookies != null) {
this.cookies.putAll(cookies);
}
@ -126,18 +94,6 @@ class DefaultClientRequestBuilder implements ClientRequest.BodyBuilder { @@ -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 <T> ClientRequest<T> body(BodyInserter<T, ? super ClientHttpRequest> inserter) {
Assert.notNull(inserter, "'inserter' must not be null");

37
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; @@ -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 { @@ -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 { @@ -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<ClientResponse> exchange() {
ClientRequest<Void> request = this.requestBuilder.build();
ClientRequest<Void> request = this.requestBuilder.headers(this.headers).build();
return getWebClient().exchange(request);
}
@Override
public <T> Mono<ClientResponse> exchange(BodyInserter<T, ? super ClientHttpRequest> inserter) {
ClientRequest<T> request = this.requestBuilder.body(inserter);
ClientRequest<T> request = this.requestBuilder.headers(this.headers).body(inserter);
return getWebClient().exchange(request);
}
@Override
public <T, S extends Publisher<T>> Mono<ClientResponse> exchange(S publisher, Class<T> elementClass) {
ClientRequest<S> request = this.requestBuilder.body(publisher, elementClass);
ClientRequest<S> request = this.requestBuilder.headers(this.headers).body(publisher, elementClass);
return getWebClient().exchange(request);
}
}

29
spring-web-reactive/src/test/java/org/springframework/web/reactive/FlushingIntegrationTests.java

@ -37,15 +37,17 @@ import org.springframework.http.server.reactive.ServerHttpResponse; @@ -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 @@ -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<Void> request = ClientRequest.GET("http://localhost:" + port + "/write-and-flush").build();
Mono<String> result = this.webClient
.exchange(request)
Mono<String> 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 @@ -76,9 +81,9 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest
@Test // SPR-14991
public void writeAndAutoFlushOnComplete() {
ClientRequest<Void> request = ClientRequest.GET("http://localhost:" + port + "/write-and-complete").build();
Mono<String> result = this.webClient
.exchange(request)
Mono<String> 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 @@ -90,9 +95,9 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest
@Test // SPR-14992
public void writeAndAutoFlushBeforeComplete() {
ClientRequest<Void> request = ClientRequest.GET("http://localhost:" + port + "/write-and-never-complete").build();
Flux<String> result = this.webClient
.exchange(request)
Flux<String> result = this.operations.get()
.uri("/write-and-never-complete")
.exchange()
.flatMap(response -> response.bodyToFlux(String.class));
StepVerifier.create(result)

113
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; @@ -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; @@ -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; @@ -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 { @@ -53,12 +50,12 @@ public class DefaultClientRequestBuilderTests {
@Test
public void from() throws Exception {
ClientRequest<Void> other = ClientRequest.GET("http://example.com")
ClientRequest<Void> other = ClientRequest.method(GET, URI.create("http://example.com"))
.header("foo", "bar")
.cookie("baz", "qux").build();
ClientRequest<Void> 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 { @@ -66,112 +63,26 @@ public class DefaultClientRequestBuilderTests {
@Test
public void method() throws Exception {
URI url = new URI("http://example.com");
ClientRequest<Void> result = ClientRequest.method(HttpMethod.DELETE, url).build();
ClientRequest<Void> 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<Void> 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<Void> 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<Void> 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<Void> 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<Void> 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<Void> 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<Void> 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<Void> 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<Void> 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<Void> 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<Void> 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<Void> result = ClientRequest.GET("http://example.com")
ClientRequest<Void> 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<Void> result = ClientRequest.GET("http://example.com")
ClientRequest<Void> 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 { @@ -193,7 +104,7 @@ public class DefaultClientRequestBuilderTests {
return response.writeWith(Mono.just(buffer));
};
ClientRequest<String> result = ClientRequest.POST("http://example.com")
ClientRequest<String> result = ClientRequest.method(POST, URI.create("http://example.com"))
.body(inserter);
List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
@ -202,7 +113,7 @@ public class DefaultClientRequestBuilderTests { @@ -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());
}

10
spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java

@ -16,15 +16,19 @@ @@ -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 { @@ -33,7 +37,7 @@ public class ExchangeFilterFunctionsTests {
@Test
public void andThen() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();
ClientRequest<Void> 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 { @@ -63,7 +67,7 @@ public class ExchangeFilterFunctionsTests {
@Test
public void apply() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();
ClientRequest<Void> 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 { @@ -82,7 +86,7 @@ public class ExchangeFilterFunctionsTests {
@Test
public void basicAuthentication() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();
ClientRequest<Void> request = ClientRequest.method(GET, URI.create("http://example.com")).build();
ClientResponse response = mock(ClientResponse.class);
ExchangeFunction exchange = r -> {

11
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; @@ -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; @@ -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 { @@ -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<Void> request = ClientRequest.GET(baseUrl.toString())
.accept(MediaType.APPLICATION_JSON)
.build();
Flux<Pojo> result = this.operations.get()
.uri("/pojos")
.accept(MediaType.APPLICATION_JSON)
@ -199,7 +189,6 @@ public class WebClientIntegrationTests { @@ -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\"}"));

62
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; @@ -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 @@ -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 @@ -60,13 +65,10 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn
@Test
public void sseAsString() throws Exception {
ClientRequest<Void> request = ClientRequest
.GET("http://localhost:{port}/string", this.port)
.accept(TEXT_EVENT_STREAM)
.build();
Flux<String> result = this.webClient
.exchange(request)
Flux<String> 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 @@ -77,14 +79,10 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn
}
@Test
public void sseAsPerson() throws Exception {
ClientRequest<Void> request =
ClientRequest
.GET("http://localhost:{port}/person", this.port)
.accept(TEXT_EVENT_STREAM)
.build();
Flux<Person> result = this.webClient
.exchange(request)
Flux<Person> 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 @@ -96,16 +94,12 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn
@Test
public void sseAsEvent() throws Exception {
ClientRequest<Void> request =
ClientRequest
.GET("http://localhost:{port}/event", this.port)
.accept(TEXT_EVENT_STREAM)
.build();
ResolvableType type = ResolvableType.forClassWithGenerics(ServerSentEvent.class, String.class);
Flux<ServerSentEvent<String>> result = this.webClient
.exchange(request)
.flatMap(response -> response.body(toFlux(type)));
Flux<ServerSentEvent<String>> 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 -> {

75
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; @@ -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; @@ -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 { @@ -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 { @@ -74,14 +79,11 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
@Test
public void sseAsString() throws Exception {
ClientRequest<Void> request = ClientRequest
.GET("http://localhost:{port}/sse/string", this.port)
.accept(TEXT_EVENT_STREAM)
.build();
Flux<String> result = this.webClient
.exchange(request)
.flatMap(response -> response.body(toFlux(String.class)));
Flux<String> 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 { @@ -91,15 +93,11 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
}
@Test
public void sseAsPerson() throws Exception {
ClientRequest<Void> request =
ClientRequest
.GET("http://localhost:{port}/sse/person", this.port)
.accept(TEXT_EVENT_STREAM)
.build();
Flux<Person> result = this.webClient
.exchange(request)
.flatMap(response -> response.body(toFlux(Person.class)));
Flux<Person> 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 { @@ -110,15 +108,11 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
@Test
public void sseAsEvent() throws Exception {
ClientRequest<Void> request =
ClientRequest
.GET("http://localhost:{port}/sse/event", this.port)
.accept(TEXT_EVENT_STREAM)
.build();
ResolvableType type = ResolvableType.forClassWithGenerics(ServerSentEvent.class, String.class);
Flux<ServerSentEvent<String>> result = this.webClient
.exchange(request)
ResolvableType type = forClassWithGenerics(ServerSentEvent.class, String.class);
Flux<ServerSentEvent<String>> 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 { @@ -142,15 +136,12 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
@Test
public void sseAsEventWithoutAcceptHeader() throws Exception {
ClientRequest<Void> request =
ClientRequest
.GET("http://localhost:{port}/sse/event", this.port)
Flux<ServerSentEvent<String>> result = this.operations.get()
.uri("/event")
.accept(TEXT_EVENT_STREAM)
.build();
Flux<ServerSentEvent<String>> 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 -> {

Loading…
Cancel
Save