Rename WebClientOperations to WebClient
This commit makes the following changes: - Merge WebClient into ExchangeFunction. Static methods on WebClient have been moved to the utility class ExchangeFunctions, similar to RouterFunctions operates on the server side. - Renamed WebClientOperations to WebClient. - Renamed WebClientStrategies to ExchangeStrategies
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,12 +30,12 @@ import org.springframework.web.reactive.function.BodyInserter;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* {@link ExchangeFunction}. Instances of this interface can be created via static
|
||||
* builder methods.
|
||||
*
|
||||
* <p>Note that applications are more likely to perform requests through
|
||||
* {@link WebClientOperations} rather than using this directly.
|
||||
* :
|
||||
* {@link WebClient} rather than using this directly.
|
||||
*
|
||||
* @param <T> the type of the body that this request contains
|
||||
* @author Brian Clozel
|
||||
* @author Arjen Poutsma
|
||||
@@ -75,7 +75,7 @@ public interface ClientRequest<T> {
|
||||
* @param strategies the strategies to use when writing
|
||||
* @return {@code Mono<Void>} to indicate when writing is complete
|
||||
*/
|
||||
Mono<Void> writeTo(ClientHttpRequest request, WebClientStrategies strategies);
|
||||
Mono<Void> writeTo(ClientHttpRequest request, ExchangeStrategies strategies);
|
||||
|
||||
|
||||
// Static builder methods
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,7 +30,7 @@ import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.web.reactive.function.BodyExtractor;
|
||||
|
||||
/**
|
||||
* Represents an HTTP response, as returned by the {@link WebClient}.
|
||||
* Represents an HTTP response, as returned by the {@link ExchangeFunction}.
|
||||
* Access to headers and body is offered by {@link Headers} and
|
||||
* {@link #body(BodyExtractor)}, {@link #bodyToMono(Class)}, {@link #bodyToFlux(Class)}
|
||||
* respectively.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -154,7 +154,7 @@ class DefaultClientRequestBuilder implements ClientRequest.Builder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> writeTo(ClientHttpRequest request, WebClientStrategies strategies) {
|
||||
public Mono<Void> writeTo(ClientHttpRequest request, ExchangeStrategies strategies) {
|
||||
HttpHeaders requestHeaders = request.getHeaders();
|
||||
if (!this.headers.isEmpty()) {
|
||||
this.headers.entrySet().stream()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,10 +49,10 @@ class DefaultClientResponse implements ClientResponse {
|
||||
|
||||
private final Headers headers;
|
||||
|
||||
private final WebClientStrategies strategies;
|
||||
private final ExchangeStrategies strategies;
|
||||
|
||||
|
||||
public DefaultClientResponse(ClientHttpResponse response, WebClientStrategies strategies) {
|
||||
public DefaultClientResponse(ClientHttpResponse response, ExchangeStrategies strategies) {
|
||||
this.response = response;
|
||||
this.strategies = strategies;
|
||||
this.headers = new DefaultHeaders();
|
||||
|
||||
@@ -46,22 +46,22 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link WebClientStrategies.Builder}.
|
||||
* Default implementation of {@link ExchangeStrategies.Builder}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
class DefaultWebClientStrategiesBuilder implements WebClientStrategies.Builder {
|
||||
class DefaultExchangeStrategiesBuilder implements ExchangeStrategies.Builder {
|
||||
|
||||
private static final boolean jackson2Present =
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
|
||||
DefaultWebClientStrategiesBuilder.class.getClassLoader()) &&
|
||||
DefaultExchangeStrategiesBuilder.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
|
||||
DefaultWebClientStrategiesBuilder.class.getClassLoader());
|
||||
DefaultExchangeStrategiesBuilder.class.getClassLoader());
|
||||
|
||||
private static final boolean jaxb2Present =
|
||||
ClassUtils.isPresent("javax.xml.bind.Binder",
|
||||
DefaultWebClientStrategiesBuilder.class.getClassLoader());
|
||||
DefaultExchangeStrategiesBuilder.class.getClassLoader());
|
||||
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders = new ArrayList<>();
|
||||
@@ -100,43 +100,43 @@ class DefaultWebClientStrategiesBuilder implements WebClientStrategies.Builder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClientStrategies.Builder messageReader(HttpMessageReader<?> messageReader) {
|
||||
public ExchangeStrategies.Builder messageReader(HttpMessageReader<?> messageReader) {
|
||||
Assert.notNull(messageReader, "'messageReader' must not be null");
|
||||
this.messageReaders.add(messageReader);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClientStrategies.Builder decoder(Decoder<?> decoder) {
|
||||
public ExchangeStrategies.Builder decoder(Decoder<?> decoder) {
|
||||
Assert.notNull(decoder, "'decoder' must not be null");
|
||||
return messageReader(new DecoderHttpMessageReader<>(decoder));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClientStrategies.Builder messageWriter(HttpMessageWriter<?> messageWriter) {
|
||||
public ExchangeStrategies.Builder messageWriter(HttpMessageWriter<?> messageWriter) {
|
||||
Assert.notNull(messageWriter, "'messageWriter' must not be null");
|
||||
this.messageWriters.add(messageWriter);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClientStrategies.Builder encoder(Encoder<?> encoder) {
|
||||
public ExchangeStrategies.Builder encoder(Encoder<?> encoder) {
|
||||
Assert.notNull(encoder, "'encoder' must not be null");
|
||||
return messageWriter(new EncoderHttpMessageWriter<>(encoder));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClientStrategies build() {
|
||||
return new DefaultWebClientStrategies(this.messageReaders, this.messageWriters);
|
||||
public ExchangeStrategies build() {
|
||||
return new DefaultExchangeStrategies(this.messageReaders, this.messageWriters);
|
||||
}
|
||||
|
||||
private static class DefaultWebClientStrategies implements WebClientStrategies {
|
||||
private static class DefaultExchangeStrategies implements ExchangeStrategies {
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
private final List<HttpMessageWriter<?>> messageWriters;
|
||||
|
||||
public DefaultWebClientStrategies(
|
||||
public DefaultExchangeStrategies(
|
||||
List<HttpMessageReader<?>> messageReaders,
|
||||
List<HttpMessageWriter<?>> messageWriters) {
|
||||
this.messageReaders = unmodifiableCopy(messageReaders);
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function.client;
|
||||
|
||||
import java.net.URI;
|
||||
@@ -38,26 +39,26 @@ import org.springframework.web.util.UriBuilderFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of {@link WebClientOperations}.
|
||||
* Default implementation of {@link WebClient}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
class DefaultWebClientOperations implements WebClientOperations {
|
||||
class DefaultWebClient implements WebClient {
|
||||
|
||||
private final WebClient webClient;
|
||||
private final ExchangeFunction exchangeFunction;
|
||||
|
||||
private final UriBuilderFactory uriBuilderFactory;
|
||||
|
||||
|
||||
DefaultWebClientOperations(WebClient webClient, UriBuilderFactory factory) {
|
||||
this.webClient = webClient;
|
||||
DefaultWebClient(ExchangeFunction exchangeFunction, UriBuilderFactory factory) {
|
||||
this.exchangeFunction = exchangeFunction;
|
||||
this.uriBuilderFactory = (factory != null ? factory : new DefaultUriBuilderFactory());
|
||||
}
|
||||
|
||||
|
||||
private WebClient getWebClient() {
|
||||
return this.webClient;
|
||||
private ExchangeFunction getExchangeFunction() {
|
||||
return this.exchangeFunction;
|
||||
}
|
||||
|
||||
private UriBuilderFactory getUriBuilderFactory() {
|
||||
@@ -107,9 +108,9 @@ class DefaultWebClientOperations implements WebClientOperations {
|
||||
|
||||
|
||||
@Override
|
||||
public WebClientOperations filter(ExchangeFilterFunction filterFunction) {
|
||||
WebClient filteredWebClient = this.webClient.filter(filterFunction);
|
||||
return new DefaultWebClientOperations(filteredWebClient, this.uriBuilderFactory);
|
||||
public WebClient filter(ExchangeFilterFunction filterFunction) {
|
||||
ExchangeFunction filteredExchangeFunction = this.exchangeFunction.filter(filterFunction);
|
||||
return new DefaultWebClient(filteredExchangeFunction, this.uriBuilderFactory);
|
||||
}
|
||||
|
||||
|
||||
@@ -219,19 +220,19 @@ class DefaultWebClientOperations implements WebClientOperations {
|
||||
@Override
|
||||
public Mono<ClientResponse> exchange() {
|
||||
ClientRequest<Void> request = this.requestBuilder.headers(this.headers).build();
|
||||
return getWebClient().exchange(request);
|
||||
return getExchangeFunction().exchange(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<ClientResponse> exchange(BodyInserter<T, ? super ClientHttpRequest> inserter) {
|
||||
ClientRequest<T> request = this.requestBuilder.headers(this.headers).body(inserter);
|
||||
return getWebClient().exchange(request);
|
||||
return getExchangeFunction().exchange(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, S extends Publisher<T>> Mono<ClientResponse> exchange(S publisher, Class<T> elementClass) {
|
||||
ClientRequest<S> request = this.requestBuilder.headers(this.headers).body(publisher, elementClass);
|
||||
return getWebClient().exchange(request);
|
||||
return getExchangeFunction().exchange(request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,101 +16,37 @@
|
||||
|
||||
package org.springframework.web.reactive.function.client;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.UriBuilderFactory;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link WebClient.Builder}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
|
||||
private ClientHttpConnector clientHttpConnector;
|
||||
private final ExchangeFunction exchangeFunction;
|
||||
|
||||
private WebClientStrategies strategies = WebClientStrategies.withDefaults();
|
||||
|
||||
private ExchangeFilterFunction filter = new NoOpFilter();
|
||||
private UriBuilderFactory uriBuilderFactory;
|
||||
|
||||
|
||||
public DefaultWebClientBuilder(ClientHttpConnector clientHttpConnector) {
|
||||
this.clientHttpConnector = clientHttpConnector;
|
||||
public DefaultWebClientBuilder(ExchangeFunction exchangeFunction) {
|
||||
Assert.notNull(exchangeFunction, "'exchangeFunction' must not be null");
|
||||
this.exchangeFunction = exchangeFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient.Builder strategies(WebClientStrategies strategies) {
|
||||
Assert.notNull(strategies, "'strategies' must not be null");
|
||||
this.strategies = strategies;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient.Builder filter(ExchangeFilterFunction filter) {
|
||||
Assert.notNull(filter, "'filter' must not be null");
|
||||
this.filter = filter.andThen(this.filter);
|
||||
public WebClient.Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory) {
|
||||
this.uriBuilderFactory = uriBuilderFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient build() {
|
||||
return new DefaultWebClient(this.clientHttpConnector, this.strategies, this.filter);
|
||||
}
|
||||
|
||||
private final static class DefaultWebClient implements WebClient {
|
||||
|
||||
private final ClientHttpConnector clientHttpConnector;
|
||||
|
||||
private final WebClientStrategies strategies;
|
||||
|
||||
private final ExchangeFilterFunction filter;
|
||||
|
||||
public DefaultWebClient(
|
||||
ClientHttpConnector clientHttpConnector,
|
||||
WebClientStrategies strategies,
|
||||
ExchangeFilterFunction filter) {
|
||||
this.clientHttpConnector = clientHttpConnector;
|
||||
this.strategies = strategies;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ClientResponse> exchange(ClientRequest<?> request) {
|
||||
Assert.notNull(request, "'request' must not be null");
|
||||
|
||||
return this.filter.filter(request, this::exchangeInternal);
|
||||
}
|
||||
|
||||
private Mono<ClientResponse> exchangeInternal(ClientRequest<?> request) {
|
||||
return this.clientHttpConnector
|
||||
.connect(request.method(), request.url(),
|
||||
clientHttpRequest -> request
|
||||
.writeTo(clientHttpRequest, this.strategies))
|
||||
.log("org.springframework.web.client.reactive", Level.FINE)
|
||||
.map(clientHttpResponse -> new DefaultClientResponse(clientHttpResponse,
|
||||
this.strategies));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient filter(ExchangeFilterFunction filter) {
|
||||
Assert.notNull(filter, "'filter' must not be null");
|
||||
|
||||
ExchangeFilterFunction composedFilter = filter.andThen(this.filter);
|
||||
|
||||
return new DefaultWebClient(this.clientHttpConnector, this.strategies, composedFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private class NoOpFilter implements ExchangeFilterFunction {
|
||||
|
||||
@Override
|
||||
public Mono<ClientResponse> filter(ClientRequest<?> request, ExchangeFunction next) {
|
||||
return next.exchange(request);
|
||||
}
|
||||
return new DefaultWebClient(this.exchangeFunction, this.uriBuilderFactory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,9 +18,21 @@ package org.springframework.web.reactive.function.client;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Represents a function that exchanges a {@linkplain ClientRequest request} for a (delayed)
|
||||
* {@linkplain ClientResponse}.
|
||||
* {@linkplain ClientResponse}. Can be used as an alternative to {@link WebClient}.
|
||||
* <p>For example:
|
||||
* <pre class="code">
|
||||
* ExchangeFunction exchangeFunction = ExchangeFunctions.create(new ReactorClientHttpConnector());
|
||||
* ClientRequest<Void> request = ClientRequest.method(HttpMethod.GET,
|
||||
* "http://example.com/resource").build();
|
||||
*
|
||||
* Mono<String> result = exchangeFunction
|
||||
* .exchange(request)
|
||||
* .then(response -> response.bodyToMono(String.class));
|
||||
* </pre>
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
@@ -31,8 +43,21 @@ public interface ExchangeFunction {
|
||||
/**
|
||||
* Exchange the given request for a response mono.
|
||||
* @param request the request to exchange
|
||||
* @return the response, wrapped in a {@code Mono}
|
||||
* @return the delayed response
|
||||
*/
|
||||
Mono<ClientResponse> exchange(ClientRequest<?> request);
|
||||
|
||||
/**
|
||||
* Filters this exchange function with the given {@code ExchangeFilterFunction}, resulting in a
|
||||
* filtered {@code ExchangeFunction}.
|
||||
* @param filter the filter to apply to this exchange
|
||||
* @return the filtered exchange
|
||||
* @see ExchangeFilterFunction#apply(ExchangeFunction)
|
||||
*/
|
||||
default ExchangeFunction filter(ExchangeFilterFunction filter) {
|
||||
Assert.notNull(filter, "'filter' must not be null");
|
||||
|
||||
return filter.apply(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.util.logging.Level;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Exposes request-response exchange functionality, such as to
|
||||
* {@linkplain #create(ClientHttpConnector) create} an {@code ExchangeFunction} given a
|
||||
* {@code ClientHttpConnector}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class ExchangeFunctions {
|
||||
|
||||
/**
|
||||
* Create a new {@link ExchangeFunction} with the given connector. This method uses
|
||||
* {@linkplain ExchangeStrategies#withDefaults() default strategies}.
|
||||
* @param connector the connector to create connections
|
||||
* @return the created function
|
||||
*/
|
||||
public static ExchangeFunction create(ClientHttpConnector connector) {
|
||||
return create(connector, ExchangeStrategies.withDefaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ExchangeFunction} with the given connector and strategies.
|
||||
* @param connector the connector to create connections
|
||||
* @param strategies the strategies to use
|
||||
* @return the created function
|
||||
*/
|
||||
public static ExchangeFunction create(ClientHttpConnector connector,
|
||||
ExchangeStrategies strategies) {
|
||||
Assert.notNull(connector, "'connector' must not be null");
|
||||
Assert.notNull(strategies, "'strategies' must not be null");
|
||||
|
||||
return new DefaultExchangeFunction(connector, strategies);
|
||||
}
|
||||
|
||||
|
||||
private static class DefaultExchangeFunction implements ExchangeFunction {
|
||||
|
||||
private final ClientHttpConnector connector;
|
||||
|
||||
private final ExchangeStrategies strategies;
|
||||
|
||||
public DefaultExchangeFunction(
|
||||
ClientHttpConnector connector,
|
||||
ExchangeStrategies strategies) {
|
||||
this.connector = connector;
|
||||
this.strategies = strategies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ClientResponse> exchange(ClientRequest<?> request) {
|
||||
Assert.notNull(request, "'request' must not be null");
|
||||
|
||||
return this.connector
|
||||
.connect(request.method(), request.url(),
|
||||
clientHttpRequest -> request.writeTo(clientHttpRequest, this.strategies))
|
||||
.log("org.springframework.web.reactive.function.client", Level.FINE)
|
||||
.map(clientHttpResponse -> new DefaultClientResponse(clientHttpResponse,
|
||||
this.strategies));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,17 +27,17 @@ import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Defines the strategies to be used by the {@link WebClient}. An instance of
|
||||
* Defines the strategies for invoking {@link ExchangeFunction}s. An instance of
|
||||
* this class is immutable; instances are typically created through the mutable {@link Builder}:
|
||||
* either through {@link #builder()} to set up default strategies, or {@link #empty()} to start from
|
||||
* scratch. Alternatively, {@code WebClientStrategies} instances can be created through
|
||||
* scratch. Alternatively, {@code ExchangeStrategies} instances can be created through
|
||||
* {@link #of(Supplier, Supplier)}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface WebClientStrategies {
|
||||
public interface ExchangeStrategies {
|
||||
|
||||
// Instance methods
|
||||
|
||||
@@ -59,38 +59,38 @@ public interface WebClientStrategies {
|
||||
// Static methods
|
||||
|
||||
/**
|
||||
* Return a new {@code WebClientStrategies} with default initialization.
|
||||
* @return the new {@code WebClientStrategies}
|
||||
* Return a new {@code ExchangeStrategies} with default initialization.
|
||||
* @return the new {@code ExchangeStrategies}
|
||||
*/
|
||||
static WebClientStrategies withDefaults() {
|
||||
static ExchangeStrategies withDefaults() {
|
||||
return builder().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@code WebClientStrategies} based on the given
|
||||
* Return a new {@code ExchangeStrategies} based on the given
|
||||
* {@linkplain ApplicationContext application context}.
|
||||
* The returned supplier will search for all {@link HttpMessageReader}, and
|
||||
* {@link HttpMessageWriter} instances in the given application context and return them for
|
||||
* {@link #messageReaders()}, and {@link #messageWriters()} respectively.
|
||||
* @param applicationContext the application context to base the strategies on
|
||||
* @return the new {@code WebClientStrategies}
|
||||
* @return the new {@code ExchangeStrategies}
|
||||
*/
|
||||
static WebClientStrategies of(ApplicationContext applicationContext) {
|
||||
static ExchangeStrategies of(ApplicationContext applicationContext) {
|
||||
return builder(applicationContext).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@code WebClientStrategies} described by the given supplier functions.
|
||||
* Return a new {@code ExchangeStrategies} described by the given supplier functions.
|
||||
* All provided supplier function parameters can be {@code null} to indicate an empty
|
||||
* stream is to be returned.
|
||||
* @param messageReaders the supplier function for {@link HttpMessageReader} instances (can be {@code null})
|
||||
* @param messageWriters the supplier function for {@link HttpMessageWriter} instances (can be {@code null})
|
||||
* @return the new {@code WebClientStrategies}
|
||||
* @return the new {@code ExchangeStrategies}
|
||||
*/
|
||||
static WebClientStrategies of(Supplier<Stream<HttpMessageReader<?>>> messageReaders,
|
||||
static ExchangeStrategies of(Supplier<Stream<HttpMessageReader<?>>> messageReaders,
|
||||
Supplier<Stream<HttpMessageWriter<?>>> messageWriters) {
|
||||
|
||||
return new WebClientStrategies() {
|
||||
return new ExchangeStrategies() {
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() {
|
||||
return checkForNull(messageReaders);
|
||||
@@ -109,11 +109,11 @@ public interface WebClientStrategies {
|
||||
// Builder methods
|
||||
|
||||
/**
|
||||
* Return a mutable builder for a {@code WebClientStrategies} with default initialization.
|
||||
* Return a mutable builder for a {@code ExchangeStrategies} with default initialization.
|
||||
* @return the builder
|
||||
*/
|
||||
static Builder builder() {
|
||||
DefaultWebClientStrategiesBuilder builder = new DefaultWebClientStrategiesBuilder();
|
||||
DefaultExchangeStrategiesBuilder builder = new DefaultExchangeStrategiesBuilder();
|
||||
builder.defaultConfiguration();
|
||||
return builder;
|
||||
}
|
||||
@@ -128,22 +128,22 @@ public interface WebClientStrategies {
|
||||
*/
|
||||
static Builder builder(ApplicationContext applicationContext) {
|
||||
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
||||
DefaultWebClientStrategiesBuilder builder = new DefaultWebClientStrategiesBuilder();
|
||||
DefaultExchangeStrategiesBuilder builder = new DefaultExchangeStrategiesBuilder();
|
||||
builder.applicationContext(applicationContext);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mutable, empty builder for a {@code WebClientStrategies}.
|
||||
* Return a mutable, empty builder for a {@code ExchangeStrategies}.
|
||||
* @return the builder
|
||||
*/
|
||||
static Builder empty() {
|
||||
return new DefaultWebClientStrategiesBuilder();
|
||||
return new DefaultExchangeStrategiesBuilder();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A mutable builder for a {@link WebClientStrategies}.
|
||||
* A mutable builder for a {@link ExchangeStrategies}.
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
@@ -180,10 +180,10 @@ public interface WebClientStrategies {
|
||||
Builder encoder(Encoder<?> encoder);
|
||||
|
||||
/**
|
||||
* Builds the {@link WebClientStrategies}.
|
||||
* Builds the {@link ExchangeStrategies}.
|
||||
* @return the built strategies
|
||||
*/
|
||||
WebClientStrategies build();
|
||||
ExchangeStrategies build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,43 +16,97 @@
|
||||
|
||||
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.ClientHttpConnector;
|
||||
import org.springframework.util.Assert;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Reactive Web client supporting the HTTP/1.1 protocol. Main entry point is through the
|
||||
* {@link #exchange(ClientRequest)} method.
|
||||
* The main class for performing Web requests.
|
||||
*
|
||||
* <p>For example:
|
||||
* <pre class="code">
|
||||
* WebClient client = WebClient.create(new ReactorClientHttpConnector());
|
||||
* ClientRequest<Void> request = ClientRequest.GET("http://example.com/resource").build();
|
||||
*
|
||||
* Mono<String> result = client
|
||||
* .exchange(request)
|
||||
* .then(response -> response.bodyToMono(String.class));
|
||||
* // Create ExchangeFunction (application-wide)
|
||||
*
|
||||
* ClientHttpConnector connector = new ReactorClientHttpConnector();
|
||||
* ExchangeFunction exchangeFunction = ExchangeFunctions.create(connector);
|
||||
*
|
||||
* // Create WebClient (per base URI)
|
||||
*
|
||||
* String baseUri = "http://abc.com";
|
||||
* UriBuilderFactory factory = new DefaultUriBuilderFactory(baseUri);
|
||||
* WebClient operations = WebClient.create(exchangeFunction, factory);
|
||||
*
|
||||
* // Perform requests...
|
||||
*
|
||||
* Mono<String> result = operations.get()
|
||||
* .uri("/foo")
|
||||
* .exchange()
|
||||
* .then(response -> response.bodyToMono(String.class));
|
||||
* </pre>
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface WebClient extends ExchangeFunction {
|
||||
public interface WebClient {
|
||||
|
||||
/**
|
||||
* Exchange the given request for a response mono. Invoking this method performs the actual
|
||||
* HTTP request/response exchange.
|
||||
* @param request the request to exchange
|
||||
* @return the response, wrapped in a {@code Mono}
|
||||
* Prepare an HTTP GET request.
|
||||
* @return a spec for specifying the target URL
|
||||
*/
|
||||
@Override
|
||||
Mono<ClientResponse> exchange(ClientRequest<?> request);
|
||||
UriSpec get();
|
||||
|
||||
/**
|
||||
* Filters this client with the given {@code ExchangeFilterFunction}, resulting in a filtered
|
||||
* {@code WebClient}.
|
||||
* 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)
|
||||
@@ -60,52 +114,204 @@ public interface WebClient extends ExchangeFunction {
|
||||
WebClient filter(ExchangeFilterFunction filterFunction);
|
||||
|
||||
|
||||
// Static, factory methods
|
||||
|
||||
/**
|
||||
* Create a new instance of {@code WebClient} with the given connector. This method uses
|
||||
* {@linkplain WebClientStrategies#withDefaults() default strategies}.
|
||||
* @param connector the connector to create connections
|
||||
* @return the created client
|
||||
* Create {@code WebClient} that uses the given {@link ClientHttpConnector}.
|
||||
* @param connector the underlying connector to use
|
||||
*/
|
||||
static WebClient create(ClientHttpConnector connector) {
|
||||
return builder(connector).build();
|
||||
return create(ExchangeFunctions.create(connector));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a builder for a {@code WebClient}.
|
||||
* @param connector the connector to create connections
|
||||
* @return a web client builder
|
||||
* Create {@code WebClient} that uses the given {@link ClientHttpConnector} and
|
||||
* {@link ExchangeStrategies}.
|
||||
* @param connector the underlying connector to use
|
||||
* @param strategies the strategies to use
|
||||
*/
|
||||
static Builder builder(ClientHttpConnector connector) {
|
||||
Assert.notNull(connector, "'connector' must not be null");
|
||||
return new DefaultWebClientBuilder(connector);
|
||||
static WebClient create(ClientHttpConnector connector, ExchangeStrategies strategies) {
|
||||
return create(ExchangeFunctions.create(connector, strategies));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@code WebClient} that wraps the given {@link ExchangeFunction}.
|
||||
* @param exchangeFunction the underlying exchange function to use
|
||||
*/
|
||||
static WebClient create(ExchangeFunction exchangeFunction) {
|
||||
return builder(exchangeFunction).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@code WebClient} with a builder for additional
|
||||
* configuration options.
|
||||
* @param exchangeFunction the underlying exchange function to use
|
||||
*/
|
||||
static WebClient.Builder builder(ExchangeFunction exchangeFunction) {
|
||||
return new DefaultWebClientBuilder(exchangeFunction);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A mutable builder for a {@link WebClient}.
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Replaces the default strategies with the ones provided by the given
|
||||
* {@code WebClientStrategies}.
|
||||
* @param strategies the strategies to use
|
||||
* @return this 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 strategies(WebClientStrategies strategies);
|
||||
Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory);
|
||||
|
||||
/**
|
||||
* Adds a filter function <strong>before</strong> the currently registered filters (if any).
|
||||
* @param filter the filter to add
|
||||
* @return this builder
|
||||
*/
|
||||
Builder filter(ExchangeFilterFunction filter);
|
||||
|
||||
/**
|
||||
* Builds the {@code WebClient}.
|
||||
* @return the built client
|
||||
* Builder the {@link WebClient} instance.
|
||||
*/
|
||||
WebClient 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,19 +36,20 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.bootstrap.RxNettyHttpServer;
|
||||
import org.springframework.web.reactive.function.BodyExtractors;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunction;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunctions;
|
||||
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.*;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
|
||||
private WebClientOperations operations;
|
||||
private WebClient webClient;
|
||||
|
||||
|
||||
@Before
|
||||
@@ -58,15 +59,15 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest
|
||||
|
||||
super.setup();
|
||||
|
||||
WebClient client = WebClient.create(new ReactorClientHttpConnector());
|
||||
ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector());
|
||||
UriBuilderFactory factory = new DefaultUriBuilderFactory("http://localhost:" + this.port);
|
||||
this.operations = WebClientOperations.builder(client).uriBuilderFactory(factory).build();
|
||||
this.webClient = WebClient.builder(exchange).uriBuilderFactory(factory).build();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void writeAndFlushWith() throws Exception {
|
||||
Mono<String> result = this.operations.get()
|
||||
Mono<String> result = this.webClient.get()
|
||||
.uri("/write-and-flush")
|
||||
.exchange()
|
||||
.flatMap(response -> response.body(BodyExtractors.toFlux(String.class)))
|
||||
@@ -81,7 +82,7 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest
|
||||
|
||||
@Test // SPR-14991
|
||||
public void writeAndAutoFlushOnComplete() {
|
||||
Mono<String> result = this.operations.get()
|
||||
Mono<String> result = this.webClient.get()
|
||||
.uri("/write-and-complete")
|
||||
.exchange()
|
||||
.flatMap(response -> response.bodyToFlux(String.class))
|
||||
@@ -95,7 +96,7 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest
|
||||
|
||||
@Test // SPR-14992
|
||||
public void writeAndAutoFlushBeforeComplete() {
|
||||
Flux<String> result = this.operations.get()
|
||||
Flux<String> result = this.webClient.get()
|
||||
.uri("/write-and-never-complete")
|
||||
.exchange()
|
||||
.flatMap(response -> response.bodyToFlux(String.class));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -83,7 +83,7 @@ public class DefaultClientRequestBuilderTests {
|
||||
.build();
|
||||
|
||||
MockClientHttpRequest request = new MockClientHttpRequest(GET, "/");
|
||||
WebClientStrategies strategies = mock(WebClientStrategies.class);
|
||||
ExchangeStrategies strategies = mock(ExchangeStrategies.class);
|
||||
|
||||
result.writeTo(request, strategies).block();
|
||||
|
||||
@@ -110,7 +110,7 @@ public class DefaultClientRequestBuilderTests {
|
||||
List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
|
||||
messageWriters.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
|
||||
|
||||
WebClientStrategies strategies = mock(WebClientStrategies.class);
|
||||
ExchangeStrategies strategies = mock(ExchangeStrategies.class);
|
||||
when(strategies.messageWriters()).thenReturn(messageWriters::stream);
|
||||
|
||||
MockClientHttpRequest request = new MockClientHttpRequest(GET, "/");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -55,7 +55,7 @@ public class DefaultClientResponseTests {
|
||||
|
||||
private ClientHttpResponse mockResponse;
|
||||
|
||||
private WebClientStrategies mockWebClientStrategies;
|
||||
private ExchangeStrategies mockExchangeStrategies;
|
||||
|
||||
private DefaultClientResponse defaultClientResponse;
|
||||
|
||||
@@ -63,9 +63,9 @@ public class DefaultClientResponseTests {
|
||||
@Before
|
||||
public void createMocks() {
|
||||
mockResponse = mock(ClientHttpResponse.class);
|
||||
mockWebClientStrategies = mock(WebClientStrategies.class);
|
||||
mockExchangeStrategies = mock(ExchangeStrategies.class);
|
||||
|
||||
defaultClientResponse = new DefaultClientResponse(mockResponse, mockWebClientStrategies);
|
||||
defaultClientResponse = new DefaultClientResponse(mockResponse, mockExchangeStrategies);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -110,7 +110,7 @@ public class DefaultClientResponseTests {
|
||||
|
||||
Set<HttpMessageReader<?>> messageReaders = Collections
|
||||
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
|
||||
when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream);
|
||||
when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream);
|
||||
|
||||
Mono<String> resultMono = defaultClientResponse.body(toMono(String.class));
|
||||
assertEquals("foo", resultMono.block());
|
||||
@@ -131,7 +131,7 @@ public class DefaultClientResponseTests {
|
||||
|
||||
Set<HttpMessageReader<?>> messageReaders = Collections
|
||||
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
|
||||
when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream);
|
||||
when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream);
|
||||
|
||||
Mono<String> resultMono = defaultClientResponse.bodyToMono(String.class);
|
||||
assertEquals("foo", resultMono.block());
|
||||
@@ -146,7 +146,7 @@ public class DefaultClientResponseTests {
|
||||
|
||||
Set<HttpMessageReader<?>> messageReaders = Collections
|
||||
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
|
||||
when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream);
|
||||
when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream);
|
||||
|
||||
Mono<String> resultMono = defaultClientResponse.bodyToMono(String.class);
|
||||
|
||||
@@ -170,7 +170,7 @@ public class DefaultClientResponseTests {
|
||||
|
||||
Set<HttpMessageReader<?>> messageReaders = Collections
|
||||
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
|
||||
when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream);
|
||||
when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream);
|
||||
|
||||
Flux<String> resultFlux = defaultClientResponse.bodyToFlux(String.class);
|
||||
Mono<List<String>> result = resultFlux.collectList();
|
||||
@@ -186,7 +186,7 @@ public class DefaultClientResponseTests {
|
||||
|
||||
Set<HttpMessageReader<?>> messageReaders = Collections
|
||||
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
|
||||
when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream);
|
||||
when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream);
|
||||
|
||||
Flux<String> resultFlux = defaultClientResponse.bodyToFlux(String.class);
|
||||
StepVerifier.create(resultFlux)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,11 +42,11 @@ import static org.junit.Assert.assertTrue;
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class WebClientStrategiesTests {
|
||||
public class ExchangeStrategiesTests {
|
||||
|
||||
@Test
|
||||
public void empty() {
|
||||
WebClientStrategies strategies = WebClientStrategies.empty().build();
|
||||
ExchangeStrategies strategies = ExchangeStrategies.empty().build();
|
||||
assertEquals(Optional.empty(), strategies.messageReaders().get().findFirst());
|
||||
assertEquals(Optional.empty(), strategies.messageWriters().get().findFirst());
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class WebClientStrategiesTests {
|
||||
HttpMessageReader<?> messageReader = new DummyMessageReader();
|
||||
HttpMessageWriter<?> messageWriter = new DummyMessageWriter();
|
||||
|
||||
WebClientStrategies strategies = WebClientStrategies.of(
|
||||
ExchangeStrategies strategies = ExchangeStrategies.of(
|
||||
() -> Stream.of(messageReader),
|
||||
() -> Stream.of(messageWriter));
|
||||
|
||||
@@ -74,7 +74,7 @@ public class WebClientStrategiesTests {
|
||||
applicationContext.registerSingleton("messageReader", DummyMessageReader.class);
|
||||
applicationContext.refresh();
|
||||
|
||||
WebClientStrategies strategies = WebClientStrategies.of(applicationContext);
|
||||
ExchangeStrategies strategies = ExchangeStrategies.of(applicationContext);
|
||||
assertTrue(strategies.messageReaders().get()
|
||||
.allMatch(r -> r instanceof DummyMessageReader));
|
||||
assertTrue(strategies.messageWriters().get()
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -43,7 +43,7 @@ import static org.junit.Assert.assertThat;
|
||||
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
|
||||
|
||||
/**
|
||||
* Integration tests using a {@link WebClient} through {@link WebClientOperations}.
|
||||
* Integration tests using a {@link ExchangeFunction} through {@link WebClient}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -52,17 +52,17 @@ public class WebClientIntegrationTests {
|
||||
|
||||
private MockWebServer server;
|
||||
|
||||
private WebClientOperations operations;
|
||||
private WebClient webClient;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.server = new MockWebServer();
|
||||
|
||||
WebClient webClient = WebClient.create(new ReactorClientHttpConnector());
|
||||
ExchangeFunction exchangeFunction = ExchangeFunctions.create(new ReactorClientHttpConnector());
|
||||
UriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(this.server.url("/").toString());
|
||||
|
||||
this.operations = WebClientOperations.builder(webClient)
|
||||
this.webClient = WebClient.builder(exchangeFunction)
|
||||
.uriBuilderFactory(uriBuilderFactory)
|
||||
.build();
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public class WebClientIntegrationTests {
|
||||
public void headers() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
|
||||
|
||||
Mono<HttpHeaders> result = this.operations.get()
|
||||
Mono<HttpHeaders> result = this.webClient.get()
|
||||
.uri("/greeting?name=Spring")
|
||||
.exchange()
|
||||
.map(response -> response.headers().asHttpHeaders());
|
||||
@@ -101,7 +101,7 @@ public class WebClientIntegrationTests {
|
||||
public void plainText() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setBody("Hello Spring!"));
|
||||
|
||||
Mono<String> result = this.operations.get()
|
||||
Mono<String> result = this.webClient.get()
|
||||
.uri("/greeting?name=Spring")
|
||||
.header("X-Test-Header", "testvalue")
|
||||
.exchange()
|
||||
@@ -125,7 +125,7 @@ public class WebClientIntegrationTests {
|
||||
this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json")
|
||||
.setBody(content));
|
||||
|
||||
Mono<String> result = this.operations.get()
|
||||
Mono<String> result = this.webClient.get()
|
||||
.uri("/json")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
@@ -147,7 +147,7 @@ public class WebClientIntegrationTests {
|
||||
this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json")
|
||||
.setBody("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}"));
|
||||
|
||||
Mono<Pojo> result = this.operations.get()
|
||||
Mono<Pojo> result = this.webClient.get()
|
||||
.uri("/pojo")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
@@ -169,7 +169,7 @@ public class WebClientIntegrationTests {
|
||||
this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json")
|
||||
.setBody("[{\"bar\":\"bar1\",\"foo\":\"foo1\"},{\"bar\":\"bar2\",\"foo\":\"foo2\"}]"));
|
||||
|
||||
Flux<Pojo> result = this.operations.get()
|
||||
Flux<Pojo> result = this.webClient.get()
|
||||
.uri("/pojos")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
@@ -193,7 +193,7 @@ public class WebClientIntegrationTests {
|
||||
.setHeader("Content-Type", "application/json")
|
||||
.setBody("{\"bar\":\"BARBAR\",\"foo\":\"FOOFOO\"}"));
|
||||
|
||||
Mono<Pojo> result = this.operations.post()
|
||||
Mono<Pojo> result = this.webClient.post()
|
||||
.uri("/pojo/capitalize")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
@@ -219,7 +219,7 @@ public class WebClientIntegrationTests {
|
||||
this.server.enqueue(new MockResponse()
|
||||
.setHeader("Content-Type", "text/plain").setBody("test"));
|
||||
|
||||
Mono<String> result = this.operations.get()
|
||||
Mono<String> result = this.webClient.get()
|
||||
.uri("/test")
|
||||
.cookie("testkey", "testvalue")
|
||||
.exchange()
|
||||
@@ -241,7 +241,7 @@ public class WebClientIntegrationTests {
|
||||
this.server.enqueue(new MockResponse().setResponseCode(404)
|
||||
.setHeader("Content-Type", "text/plain").setBody("Not Found"));
|
||||
|
||||
Mono<ClientResponse> result = this.operations.get().uri("/greeting?name=Spring").exchange();
|
||||
Mono<ClientResponse> result = this.webClient.get().uri("/greeting?name=Spring").exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(response -> assertEquals(HttpStatus.NOT_FOUND, response.statusCode()))
|
||||
@@ -258,7 +258,7 @@ public class WebClientIntegrationTests {
|
||||
public void buildFilter() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
|
||||
|
||||
WebClientOperations filteredClient = this.operations.filter(
|
||||
WebClient filteredClient = this.webClient.filter(
|
||||
(request, next) -> {
|
||||
ClientRequest<?> filteredRequest = ClientRequest.from(request).header("foo", "bar").build();
|
||||
return next.exchange(filteredRequest);
|
||||
@@ -284,7 +284,7 @@ public class WebClientIntegrationTests {
|
||||
public void filter() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
|
||||
|
||||
WebClientOperations filteredClient = this.operations.filter(
|
||||
WebClient filteredClient = this.webClient.filter(
|
||||
(request, next) -> {
|
||||
ClientRequest<?> filteredRequest = ClientRequest.from(request).header("foo", "bar").build();
|
||||
return next.exchange(filteredRequest);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,8 +26,9 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunction;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunctions;
|
||||
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;
|
||||
|
||||
@@ -44,15 +45,16 @@ import static org.springframework.web.reactive.function.server.RouterFunctions.r
|
||||
*/
|
||||
public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIntegrationTests {
|
||||
|
||||
private WebClientOperations operations;
|
||||
private WebClient webClient;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
WebClient client = WebClient.create(new ReactorClientHttpConnector());
|
||||
ExchangeFunction exchangeFunction =
|
||||
ExchangeFunctions.create(new ReactorClientHttpConnector());
|
||||
UriBuilderFactory factory = new DefaultUriBuilderFactory("http://localhost:" + this.port);
|
||||
this.operations = WebClientOperations.builder(client).uriBuilderFactory(factory).build();
|
||||
this.webClient = WebClient.builder(exchangeFunction).uriBuilderFactory(factory).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,7 +67,7 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn
|
||||
|
||||
@Test
|
||||
public void sseAsString() throws Exception {
|
||||
Flux<String> result = this.operations.get()
|
||||
Flux<String> result = this.webClient.get()
|
||||
.uri("/string")
|
||||
.accept(TEXT_EVENT_STREAM)
|
||||
.exchange()
|
||||
@@ -79,7 +81,7 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn
|
||||
}
|
||||
@Test
|
||||
public void sseAsPerson() throws Exception {
|
||||
Flux<Person> result = this.operations.get()
|
||||
Flux<Person> result = this.webClient.get()
|
||||
.uri("/person")
|
||||
.accept(TEXT_EVENT_STREAM)
|
||||
.exchange()
|
||||
@@ -94,7 +96,7 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn
|
||||
|
||||
@Test
|
||||
public void sseAsEvent() throws Exception {
|
||||
Flux<ServerSentEvent<String>> result = this.operations.get()
|
||||
Flux<ServerSentEvent<String>> result = this.webClient.get()
|
||||
.uri("/event")
|
||||
.accept(TEXT_EVENT_STREAM)
|
||||
.exchange()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,8 +35,9 @@ 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.ExchangeFunction;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunctions;
|
||||
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;
|
||||
@@ -55,16 +56,16 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext wac;
|
||||
|
||||
private WebClientOperations operations;
|
||||
private WebClient webClient;
|
||||
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
WebClient client = WebClient.create(new ReactorClientHttpConnector());
|
||||
ExchangeFunction exchangeFunction = ExchangeFunctions.create(new ReactorClientHttpConnector());
|
||||
UriBuilderFactory factory = new DefaultUriBuilderFactory("http://localhost:" + this.port + "/sse");
|
||||
this.operations = WebClientOperations.builder(client).uriBuilderFactory(factory).build();
|
||||
this.webClient = WebClient.builder(exchangeFunction).uriBuilderFactory(factory).build();
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +80,7 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void sseAsString() throws Exception {
|
||||
Flux<String> result = this.operations.get()
|
||||
Flux<String> result = this.webClient.get()
|
||||
.uri("/string")
|
||||
.accept(TEXT_EVENT_STREAM)
|
||||
.exchange()
|
||||
@@ -93,7 +94,7 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
}
|
||||
@Test
|
||||
public void sseAsPerson() throws Exception {
|
||||
Flux<Person> result = this.operations.get()
|
||||
Flux<Person> result = this.webClient.get()
|
||||
.uri("/person")
|
||||
.accept(TEXT_EVENT_STREAM)
|
||||
.exchange()
|
||||
@@ -109,7 +110,7 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
@Test
|
||||
public void sseAsEvent() throws Exception {
|
||||
ResolvableType type = forClassWithGenerics(ServerSentEvent.class, String.class);
|
||||
Flux<ServerSentEvent<String>> result = this.operations.get()
|
||||
Flux<ServerSentEvent<String>> result = this.webClient.get()
|
||||
.uri("/event")
|
||||
.accept(TEXT_EVENT_STREAM)
|
||||
.exchange()
|
||||
@@ -136,7 +137,7 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void sseAsEventWithoutAcceptHeader() throws Exception {
|
||||
Flux<ServerSentEvent<String>> result = this.operations.get()
|
||||
Flux<ServerSentEvent<String>> result = this.webClient.get()
|
||||
.uri("/event")
|
||||
.accept(TEXT_EVENT_STREAM)
|
||||
.exchange()
|
||||
|
||||
Reference in New Issue
Block a user