Browse Source

Introduce WebClientOperations

Issue: SPR-15124
pull/1308/head
Rossen Stoyanchev 9 years ago
parent
commit
f6ec35394f
  1. 227
      spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperations.java
  2. 51
      spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperationsBuilder.java
  3. 295
      spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClientOperations.java
  4. 149
      spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java

227
spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperations.java

@ -0,0 +1,227 @@ @@ -0,0 +1,227 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.function.client;
import java.net.URI;
import java.nio.charset.Charset;
import java.time.ZonedDateTime;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
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.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilderFactory;
/**
* Default implementation of {@link WebClientOperations}.
*
* @author Rossen Stoyanchev
* @since 5.0
*/
class DefaultWebClientOperations implements WebClientOperations {
private final WebClient webClient;
private final UriBuilderFactory uriBuilderFactory;
DefaultWebClientOperations(WebClient webClient, UriBuilderFactory factory) {
this.webClient = webClient;
this.uriBuilderFactory = (factory != null ? factory : new DefaultUriBuilderFactory());
}
private WebClient getWebClient() {
return this.webClient;
}
private UriBuilderFactory getUriBuilderFactory() {
return this.uriBuilderFactory;
}
@Override
public UriSpec get() {
return method(HttpMethod.GET);
}
@Override
public UriSpec head() {
return method(HttpMethod.HEAD);
}
@Override
public UriSpec post() {
return method(HttpMethod.POST);
}
@Override
public UriSpec put() {
return method(HttpMethod.PUT);
}
@Override
public UriSpec patch() {
return method(HttpMethod.PATCH);
}
@Override
public UriSpec delete() {
return method(HttpMethod.DELETE);
}
@Override
public UriSpec options() {
return method(HttpMethod.OPTIONS);
}
@NotNull
private UriSpec method(HttpMethod httpMethod) {
return new DefaultUriSpec(httpMethod);
}
@Override
public WebClientOperations filter(ExchangeFilterFunction filterFunction) {
WebClient filteredWebClient = this.webClient.filter(filterFunction);
return new DefaultWebClientOperations(filteredWebClient, this.uriBuilderFactory);
}
private class DefaultUriSpec implements UriSpec {
private final HttpMethod httpMethod;
DefaultUriSpec(HttpMethod httpMethod) {
this.httpMethod = httpMethod;
}
@Override
public HeaderSpec uri(URI uri) {
return new DefaultHeaderSpec(ClientRequest.method(this.httpMethod, uri));
}
@Override
public HeaderSpec uri(String uriTemplate, Object... uriVariables) {
return uri(getUriBuilderFactory().expand(uriTemplate));
}
@Override
public HeaderSpec uri(Function<UriBuilderFactory, URI> uriFunction) {
return uri(uriFunction.apply(getUriBuilderFactory()));
}
}
private class DefaultHeaderSpec implements HeaderSpec {
private ClientRequest.BodyBuilder requestBuilder;
DefaultHeaderSpec(ClientRequest.BodyBuilder requestBuilder) {
this.requestBuilder = requestBuilder;
}
@Override
public DefaultHeaderSpec header(String headerName, String... headerValues) {
this.requestBuilder.header(headerName, headerValues);
return this;
}
@Override
public DefaultHeaderSpec headers(HttpHeaders headers) {
this.requestBuilder.headers(headers);
return this;
}
@Override
public DefaultHeaderSpec accept(MediaType... acceptableMediaTypes) {
this.requestBuilder.accept(acceptableMediaTypes);
return this;
}
@Override
public DefaultHeaderSpec acceptCharset(Charset... acceptableCharsets) {
this.requestBuilder.acceptCharset(acceptableCharsets);
return this;
}
@Override
public DefaultHeaderSpec contentType(MediaType contentType) {
this.requestBuilder.contentType(contentType);
return this;
}
@Override
public DefaultHeaderSpec contentLength(long contentLength) {
this.requestBuilder.contentLength(contentLength);
return this;
}
@Override
public DefaultHeaderSpec cookie(String name, String value) {
this.requestBuilder.cookie(name, value);
return this;
}
@Override
public DefaultHeaderSpec cookies(MultiValueMap<String, String> cookies) {
this.requestBuilder.cookies(cookies);
return this;
}
@Override
public DefaultHeaderSpec ifModifiedSince(ZonedDateTime ifModifiedSince) {
this.requestBuilder.ifModifiedSince(ifModifiedSince);
return this;
}
@Override
public DefaultHeaderSpec ifNoneMatch(String... ifNoneMatches) {
this.requestBuilder.ifNoneMatch(ifNoneMatches);
return this;
}
@Override
public Mono<ClientResponse> exchange() {
ClientRequest<Void> request = this.requestBuilder.build();
return getWebClient().exchange(request);
}
@Override
public <T> Mono<ClientResponse> exchange(BodyInserter<T, ? super ClientHttpRequest> inserter) {
ClientRequest<T> request = this.requestBuilder.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);
return getWebClient().exchange(request);
}
}
}

51
spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperationsBuilder.java

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.function.client;
import org.springframework.util.Assert;
import org.springframework.web.util.UriBuilderFactory;
/**
* Default implementation of {@link WebClientOperations.Builder}.
*
* @author Rossen Stoyanchev
* @since 5.0
*/
class DefaultWebClientOperationsBuilder implements WebClientOperations.Builder {
private final WebClient webClient;
private UriBuilderFactory uriBuilderFactory;
public DefaultWebClientOperationsBuilder(WebClient webClient) {
Assert.notNull(webClient, "WebClient is required");
this.webClient = webClient;
}
@Override
public WebClientOperations.Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory) {
this.uriBuilderFactory = uriBuilderFactory;
return this;
}
@Override
public WebClientOperations build() {
return new DefaultWebClientOperations(this.webClient, this.uriBuilderFactory);
}
}

295
spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClientOperations.java

@ -0,0 +1,295 @@ @@ -0,0 +1,295 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.function.client;
import java.net.URI;
import java.nio.charset.Charset;
import java.time.ZonedDateTime;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.util.UriBuilderFactory;
/**
* The main class for performing requests through a WebClient.
*
* <pre class="code">
*
* // Create WebClient (application-wide)
*
* ClientHttpConnector connector = new ReactorClientHttpConnector();
* WebClient webClient = WebClient.create(connector);
*
* // Create WebClientOperations (per base URI)
*
* String baseUri = "http://abc.com";
* UriBuilderFactory factory = new DefaultUriBuilderFactory(baseUri);
* WebClientOperations operations = WebClientOperations.create(webClient, factory);
*
* // Perform requests...
*
* Mono<String> result = operations.get()
* .uri("/foo")
* .exchange()
* .then(response -> response.bodyToMono(String.class));
* </pre>
*
* @author Rossen Stoyanchev
* @since 5.0
*/
public interface WebClientOperations {
/**
* Prepare an HTTP GET request.
* @return a spec for specifying the target URL
*/
UriSpec get();
/**
* Prepare an HTTP HEAD request.
* @return a spec for specifying the target URL
*/
UriSpec head();
/**
* Prepare an HTTP POST request.
* @return a spec for specifying the target URL
*/
UriSpec post();
/**
* Prepare an HTTP PUT request.
* @return a spec for specifying the target URL
*/
UriSpec put();
/**
* Prepare an HTTP PATCH request.
* @return a spec for specifying the target URL
*/
UriSpec patch();
/**
* Prepare an HTTP DELETE request.
* @return a spec for specifying the target URL
*/
UriSpec delete();
/**
* Prepare an HTTP OPTIONS request.
* @return a spec for specifying the target URL
*/
UriSpec options();
/**
* Filter the client with the given {@code ExchangeFilterFunction}.
* @param filterFunction the filter to apply to this client
* @return the filtered client
* @see ExchangeFilterFunction#apply(ExchangeFunction)
*/
WebClientOperations filter(ExchangeFilterFunction filterFunction);
// Static, factory methods
/**
* Create {@link WebClientOperations} that wraps the given {@link WebClient}.
* @param webClient the underlying client to use
*/
static WebClientOperations create(WebClient webClient) {
return builder(webClient).build();
}
/**
* Create {@link WebClientOperations} with a builder for additional
* configuration options.
* @param webClient the underlying client to use
*/
static WebClientOperations.Builder builder(WebClient webClient) {
return new DefaultWebClientOperationsBuilder(webClient);
}
/**
* A mutable builder for a {@link WebClientOperations}.
*/
interface Builder {
/**
* Configure a {@code UriBuilderFactory} for use with this client for
* example to define a common "base" URI.
* @param uriBuilderFactory the URI builder factory
*/
Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory);
/**
* Builder the {@link WebClient} instance.
*/
WebClientOperations build();
}
/**
* Contract for specifying the URI for a request.
*/
interface UriSpec {
/**
* Specify the URI using an absolute, fully constructed {@link URI}.
*/
HeaderSpec uri(URI uri);
/**
* Specify the URI for the request using a URI template and URI variables.
* If a {@link UriBuilderFactory} was configured for the client (e.g.
* with a base URI) it will be used to expand the URI template.
* @see Builder#uriBuilderFactory(UriBuilderFactory)
*/
HeaderSpec uri(String uri, Object... uriVariables);
/**
* Build the URI for the request using the {@link UriBuilderFactory}
* configured for this client.
* @see Builder#uriBuilderFactory(UriBuilderFactory)
*/
HeaderSpec uri(Function<UriBuilderFactory, URI> uriFunction);
}
/**
* Contract for specifying request headers leading up to the exchange.
*/
interface HeaderSpec {
/**
* 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
*/
HeaderSpec 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
*/
HeaderSpec acceptCharset(Charset... acceptableCharsets);
/**
* 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)
*/
HeaderSpec 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)
*/
HeaderSpec contentType(MediaType contentType);
/**
* Add a cookie with the given name and value.
* @param name the cookie name
* @param value the cookie value
* @return this builder
*/
HeaderSpec cookie(String name, String value);
/**
* Copy the given cookies into the entity's cookies map.
*
* @param cookies the existing cookies to copy from
* @return this builder
*/
HeaderSpec cookies(MultiValueMap<String, String> cookies);
/**
* 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
*/
HeaderSpec ifModifiedSince(ZonedDateTime ifModifiedSince);
/**
* Set the values of the {@code If-None-Match} header.
* @param ifNoneMatches the new value of the header
* @return this builder
*/
HeaderSpec ifNoneMatch(String... ifNoneMatches);
/**
* Add the given, single header value under the given name.
* @param headerName the header name
* @param headerValues the header value(s)
* @return this builder
*/
HeaderSpec header(String headerName, String... headerValues);
/**
* Copy the given headers into the entity's headers map.
* @param headers the existing headers to copy from
* @return this builder
*/
HeaderSpec headers(HttpHeaders headers);
/**
* Perform the request without a request body.
* @return a {@code Mono} with the response
*/
Mono<ClientResponse> exchange();
/**
* Set the body of the request to the given {@code BodyInserter} and
* perform the request.
* @param inserter the {@code BodyInserter} that writes to the request
* @param <T> the type contained in the body
* @return a {@code Mono} with the response
*/
<T> Mono<ClientResponse> exchange(BodyInserter<T, ? super ClientHttpRequest> inserter);
/**
* Set the body of the request to the given {@code Publisher} and
* perform the request.
* @param publisher the {@code Publisher} to write to the request
* @param elementClass the class of elements contained in the publisher
* @param <T> the type of the elements contained in the publisher
* @param <S> the type of the {@code Publisher}
* @return a {@code Mono} with the response
*/
<T, S extends Publisher<T>> Mono<ClientResponse> exchange(S publisher, Class<T> elementClass);
}
}

149
spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java

@ -38,37 +38,53 @@ import org.springframework.http.client.reactive.ReactorClientHttpConnector; @@ -38,37 +38,53 @@ 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;
/**
* {@link WebClient} integration tests with the {@code Flux} and {@code Mono} API.
* Integration tests using a {@link WebClient} through {@link WebClientOperations}.
*
* @author Brian Clozel
* @author Rossen Stoyanchev
*/
public class WebClientIntegrationTests {
private MockWebServer server;
private WebClient webClient;
private WebClientOperations operations;
@Before
public void setup() {
this.server = new MockWebServer();
this.webClient = WebClient.create(new ReactorClientHttpConnector());
WebClient webClient = WebClient.create(new ReactorClientHttpConnector());
UriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(this.server.url("/").toString());
this.operations = WebClientOperations.builder(webClient)
.uriBuilderFactory(uriBuilderFactory)
.build();
}
@After
public void tearDown() throws Exception {
this.server.shutdown();
}
@Test
public void headers() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
Mono<HttpHeaders> result = this.webClient
.exchange(request)
Mono<HttpHeaders> result = this.operations.get()
.uri("/greeting?name=Spring")
.exchange()
.map(response -> response.headers().asHttpHeaders());
StepVerifier.create(result)
@ -88,16 +104,13 @@ public class WebClientIntegrationTests { @@ -88,16 +104,13 @@ public class WebClientIntegrationTests {
@Test
public void plainText() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setBody("Hello Spring!"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString())
Mono<String> result = this.operations.get()
.uri("/greeting?name=Spring")
.header("X-Test-Header", "testvalue")
.build();
Mono<String> result = this.webClient
.exchange(request)
.then(response -> response.body(toMono(String.class)));
.exchange()
.then(response -> response.bodyToMono(String.class));
StepVerifier.create(result)
.expectNext("Hello Spring!")
@ -113,18 +126,15 @@ public class WebClientIntegrationTests { @@ -113,18 +126,15 @@ public class WebClientIntegrationTests {
@Test
public void jsonString() throws Exception {
HttpUrl baseUrl = server.url("/json");
String content = "{\"bar\":\"barbar\",\"foo\":\"foofoo\"}";
this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json")
.setBody(content));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString())
Mono<String> result = this.operations.get()
.uri("/json")
.accept(MediaType.APPLICATION_JSON)
.build();
Mono<String> result = this.webClient
.exchange(request)
.then(response -> response.body(toMono(String.class)));
.exchange()
.then(response -> response.bodyToMono(String.class));
StepVerifier.create(result)
.expectNext(content)
@ -139,17 +149,14 @@ public class WebClientIntegrationTests { @@ -139,17 +149,14 @@ public class WebClientIntegrationTests {
@Test
public void jsonPojoMono() throws Exception {
HttpUrl baseUrl = server.url("/pojo");
this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json")
.setBody("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString())
Mono<Pojo> result = this.operations.get()
.uri("/pojo")
.accept(MediaType.APPLICATION_JSON)
.build();
Mono<Pojo> result = this.webClient
.exchange(request)
.then(response -> response.body(toMono(Pojo.class)));
.exchange()
.then(response -> response.bodyToMono(Pojo.class));
StepVerifier.create(result)
.consumeNextWith(p -> assertEquals("barbar", p.getBar()))
@ -172,9 +179,11 @@ public class WebClientIntegrationTests { @@ -172,9 +179,11 @@ public class WebClientIntegrationTests {
.accept(MediaType.APPLICATION_JSON)
.build();
Flux<Pojo> result = this.webClient
.exchange(request)
.flatMap(response -> response.body(toFlux(Pojo.class)));
Flux<Pojo> result = this.operations.get()
.uri("/pojos")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.flatMap(response -> response.bodyToFlux(Pojo.class));
StepVerifier.create(result)
.consumeNextWith(p -> assertThat(p.getBar(), Matchers.is("bar1")))
@ -195,15 +204,12 @@ public class WebClientIntegrationTests { @@ -195,15 +204,12 @@ public class WebClientIntegrationTests {
.setHeader("Content-Type", "application/json")
.setBody("{\"bar\":\"BARBAR\",\"foo\":\"FOOFOO\"}"));
Pojo spring = new Pojo("foofoo", "barbar");
ClientRequest<Pojo> request = ClientRequest.POST(baseUrl.toString())
Mono<Pojo> result = this.operations.post()
.uri("/pojo/capitalize")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject(spring));
Mono<Pojo> result = this.webClient
.exchange(request)
.then(response -> response.body(BodyExtractors.toMono(Pojo.class)));
.exchange(fromObject(new Pojo("foofoo", "barbar")))
.then(response -> response.bodyToMono(Pojo.class));
StepVerifier.create(result)
.consumeNextWith(p -> assertEquals("BARBAR", p.getBar()))
@ -221,17 +227,14 @@ public class WebClientIntegrationTests { @@ -221,17 +227,14 @@ public class WebClientIntegrationTests {
@Test
public void cookies() throws Exception {
HttpUrl baseUrl = server.url("/test");
this.server.enqueue(new MockResponse()
.setHeader("Content-Type", "text/plain").setBody("test"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString())
Mono<String> result = this.operations.get()
.uri("/test")
.cookie("testkey", "testvalue")
.build();
Mono<String> result = this.webClient
.exchange(request)
.then(response -> response.body(toMono(String.class)));
.exchange()
.then(response -> response.bodyToMono(String.class));
StepVerifier.create(result)
.expectNext("test")
@ -246,19 +249,13 @@ public class WebClientIntegrationTests { @@ -246,19 +249,13 @@ public class WebClientIntegrationTests {
@Test
public void notFound() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setResponseCode(404)
.setHeader("Content-Type", "text/plain").setBody("Not Found"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
Mono<ClientResponse> result = this.webClient
.exchange(request);
Mono<ClientResponse> result = this.operations.get().uri("/greeting?name=Spring").exchange();
StepVerifier.create(result)
.consumeNextWith(response -> {
assertEquals(HttpStatus.NOT_FOUND, response.statusCode());
})
.consumeNextWith(response -> assertEquals(HttpStatus.NOT_FOUND, response.statusCode()))
.expectComplete()
.verify(Duration.ofSeconds(3));
@ -270,21 +267,18 @@ public class WebClientIntegrationTests { @@ -270,21 +267,18 @@ public class WebClientIntegrationTests {
@Test
public void buildFilter() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
ExchangeFilterFunction filter = (request, next) -> {
ClientRequest<?> filteredRequest = ClientRequest.from(request)
.header("foo", "bar").build();
return next.exchange(filteredRequest);
};
WebClient filteredClient = WebClient.builder(new ReactorClientHttpConnector())
.filter(filter).build();
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
WebClientOperations filteredClient = this.operations.filter(
(request, next) -> {
ClientRequest<?> filteredRequest = ClientRequest.from(request).header("foo", "bar").build();
return next.exchange(filteredRequest);
});
Mono<String> result = filteredClient.exchange(request)
.then(response -> response.body(toMono(String.class)));
Mono<String> result = filteredClient.get()
.uri("/greeting?name=Spring")
.exchange()
.then(response -> response.bodyToMono(String.class));
StepVerifier.create(result)
.expectNext("Hello Spring!")
@ -299,21 +293,18 @@ public class WebClientIntegrationTests { @@ -299,21 +293,18 @@ public class WebClientIntegrationTests {
@Test
public void filter() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
ExchangeFilterFunction filter = (request, next) -> {
ClientRequest<?> filteredRequest = ClientRequest.from(request)
.header("foo", "bar").build();
return next.exchange(filteredRequest);
};
WebClient client = WebClient.create(new ReactorClientHttpConnector());
WebClient filteredClient = client.filter(filter);
WebClientOperations filteredClient = this.operations.filter(
(request, next) -> {
ClientRequest<?> filteredRequest = ClientRequest.from(request).header("foo", "bar").build();
return next.exchange(filteredRequest);
});
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
Mono<String> result = filteredClient.exchange(request)
.then(response -> response.body(toMono(String.class)));
Mono<String> result = filteredClient.get()
.uri("/greeting?name=Spring")
.exchange()
.then(response -> response.bodyToMono(String.class));
StepVerifier.create(result)
.expectNext("Hello Spring!")
@ -326,8 +317,4 @@ public class WebClientIntegrationTests { @@ -326,8 +317,4 @@ public class WebClientIntegrationTests {
}
@After
public void tearDown() throws Exception {
this.server.shutdown();
}
}
Loading…
Cancel
Save