Update WebClient builder

Leave only one static, no-arg build() method for access to a Builder.

URI-related preferences are now exposed on the builder itself.

Improve Javadoc with base URI examples.
This commit is contained in:
Rossen Stoyanchev 2017-02-02 16:15:44 -05:00
parent acf511ac0e
commit 82a34f4b24
4 changed files with 157 additions and 90 deletions

View File

@ -22,6 +22,7 @@ import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
@ -143,6 +144,11 @@ class DefaultWebClient implements WebClient {
return uri(getUriBuilderFactory().expand(uriTemplate, uriVariables));
}
@Override
public HeaderSpec uri(String uriTemplate, Map<String, ?> uriVariables) {
return uri(getUriBuilderFactory().expand(uriTemplate, uriVariables));
}
@Override
public HeaderSpec uri(Function<UriBuilderFactory, URI> uriFunction) {
return uri(uriFunction.apply(getUriBuilderFactory()));

View File

@ -17,6 +17,7 @@
package org.springframework.web.reactive.function.client;
import java.util.Arrays;
import java.util.Map;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.reactive.ClientHttpConnector;
@ -35,49 +36,38 @@ import org.springframework.web.util.UriBuilderFactory;
*/
class DefaultWebClientBuilder implements WebClient.Builder {
private String baseUrl;
private Map<String, ?> defaultUriVariables;
private UriBuilderFactory uriBuilderFactory;
private HttpHeaders defaultHeaders;
private MultiValueMap<String, String> defaultCookies;
private ClientHttpConnector connector;
private ExchangeStrategies exchangeStrategies = ExchangeStrategies.withDefaults();
private ExchangeFunction exchangeFunction;
private HttpHeaders defaultHeaders;
private MultiValueMap<String, String> defaultCookies;
public DefaultWebClientBuilder() {
this(new DefaultUriBuilderFactory());
@Override
public WebClient.Builder baseUrl(String baseUrl) {
this.baseUrl = baseUrl;
return this;
}
public DefaultWebClientBuilder(String baseUrl) {
this(new DefaultUriBuilderFactory(baseUrl));
@Override
public WebClient.Builder defaultUriVariables(Map<String, ?> defaultUriVariables) {
this.defaultUriVariables = defaultUriVariables;
return this;
}
public DefaultWebClientBuilder(UriBuilderFactory uriBuilderFactory) {
Assert.notNull(uriBuilderFactory, "UriBuilderFactory is required.");
@Override
public WebClient.Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory) {
this.uriBuilderFactory = uriBuilderFactory;
}
@Override
public WebClient.Builder clientConnector(ClientHttpConnector connector) {
this.connector = connector;
return this;
}
@Override
public WebClient.Builder exchangeStrategies(ExchangeStrategies strategies) {
Assert.notNull(strategies, "ExchangeStrategies is required.");
this.exchangeStrategies = strategies;
return this;
}
@Override
public WebClient.Builder exchangeFunction(ExchangeFunction exchangeFunction) {
this.exchangeFunction = exchangeFunction;
return this;
}
@ -101,10 +91,40 @@ class DefaultWebClientBuilder implements WebClient.Builder {
return this;
}
@Override
public WebClient.Builder clientConnector(ClientHttpConnector connector) {
this.connector = connector;
return this;
}
@Override
public WebClient.Builder exchangeStrategies(ExchangeStrategies strategies) {
Assert.notNull(strategies, "ExchangeStrategies is required.");
this.exchangeStrategies = strategies;
return this;
}
@Override
public WebClient.Builder exchangeFunction(ExchangeFunction exchangeFunction) {
this.exchangeFunction = exchangeFunction;
return this;
}
@Override
public WebClient build() {
return new DefaultWebClient(initExchangeFunction(),
this.uriBuilderFactory, this.defaultHeaders, this.defaultCookies);
return new DefaultWebClient(initExchangeFunction(), initUriBuilderFactory(),
this.defaultHeaders, this.defaultCookies);
}
private UriBuilderFactory initUriBuilderFactory() {
if (this.uriBuilderFactory != null) {
return this.uriBuilderFactory;
}
DefaultUriBuilderFactory factory = this.baseUrl != null ?
new DefaultUriBuilderFactory(this.baseUrl) : new DefaultUriBuilderFactory();
factory.setDefaultUriVariables(this.defaultUriVariables);
return factory;
}
private ExchangeFunction initExchangeFunction() {

View File

@ -19,6 +19,7 @@ package org.springframework.web.reactive.function.client;
import java.net.URI;
import java.nio.charset.Charset;
import java.time.ZonedDateTime;
import java.util.Map;
import java.util.function.Function;
import org.reactivestreams.Publisher;
@ -30,7 +31,6 @@ import org.springframework.http.client.reactive.ClientHttpConnector;
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;
/**
@ -38,20 +38,13 @@ import org.springframework.web.util.UriBuilderFactory;
*
* <pre class="code">
*
* // Create ExchangeFunction (application-wide)
* // Initialize the client
*
* 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);
* WebClient client = WebClient.create("http://abc.com");
*
* // Perform requests...
*
* Mono<String> result = operations.get()
* Mono&#060;String&#062; result = client.get()
* .uri("/foo")
* .exchange()
* .then(response -> response.bodyToMono(String.class));
@ -118,24 +111,49 @@ public interface WebClient {
// Static, factory methods
/**
* Shortcut for:
* <pre class="code">
* WebClient client = builder().build();
* </pre>
* Create a new {@code WebClient} with no default, shared preferences across
* requests such as base URI, default headers, and others.
*/
static WebClient create() {
return new DefaultWebClientBuilder().build();
}
/**
* Shortcut for:
* Configure a base URI for requests performed through the client for
* example to avoid repeating the same host, port, base path, or even
* query parameters with every request.
*
* <p>Given the following initialization:
* <pre class="code">
* WebClient client = builder(baseUrl).build();
* WebClient client = WebClient.create("http://abc.com/v1");
* </pre>
*
* <p>A base URI is applied when using a URI template:
* <pre class="code">
*
* // GET http://abc.com/v1/accounts/43
*
* Mono&#060;Account&#062; result = client.get()
* .uri("/accounts/{id}", 43)
* .exchange()
* .then(response -> response.bodyToMono(String.class));
* </pre>
*
* <p>It is also applied when using a {@link UriBuilderFactory}:
* <pre class="code">
* // GET http://abc.com/v1/accounts?q=12
*
* Mono&#060;Account&#062; result = client.get()
* .uri(factory -> factory.uriString("/accounts").queryParam("q", "12").build())
* .exchange()
* .then(response -> response.bodyToMono(String.class));
* </pre>
*
* @param baseUrl the base URI for all requests
*/
static WebClient create(String baseUrl) {
return new DefaultWebClientBuilder(baseUrl).build();
return new DefaultWebClientBuilder().baseUrl(baseUrl).build();
}
/**
@ -145,27 +163,6 @@ public interface WebClient {
return new DefaultWebClientBuilder();
}
/**
* Obtain a {@code WebClient} builder with a base URI to be used as the
* base for expanding URI templates during exchanges. The given String
* is used to create an instance of {@link DefaultUriBuilderFactory} whose
* {@link DefaultUriBuilderFactory#DefaultUriBuilderFactory(String)
* constructor} provides more details on how the base URI is applied.
* @param baseUrl the base URI for all requests
*/
static WebClient.Builder builder(String baseUrl) {
return new DefaultWebClientBuilder(baseUrl);
}
/**
* Obtain a {@code WebClient} builder with the {@link UriBuilderFactory}
* to use for expanding URI templates during exchanges.
* @param uriBuilderFactory the factory to use
*/
static WebClient.Builder builder(UriBuilderFactory uriBuilderFactory) {
return new DefaultWebClientBuilder(uriBuilderFactory);
}
/**
* A mutable builder for a {@link WebClient}.
@ -173,30 +170,34 @@ public interface WebClient {
interface Builder {
/**
* Configure the {@link ClientHttpConnector} to use.
* <p>By default an instance of
* {@link org.springframework.http.client.reactive.ReactorClientHttpConnector
* ReactorClientHttpConnector} is created if this is not set. However a
* shared instance may be passed instead, e.g. for use with multiple
* {@code WebClient}'s targeting different base URIs.
* @param connector the connector to use
* Configure a base URI as described in {@link WebClient#create(String)
* WebClient.create(String)}.
* @see #defaultUriVariables(Map)
* @see #uriBuilderFactory(UriBuilderFactory)
*/
Builder clientConnector(ClientHttpConnector connector);
Builder baseUrl(String baseUrl);
/**
* Configure the {@link ExchangeStrategies} to use.
* <p>By default {@link ExchangeStrategies#withDefaults()} is used.
* @param strategies the strategies to use
* Configure default URI variable values that will be used when expanding
* URI templates using a {@link Map}.
* @param defaultUriVariables the default values to use
* @see #baseUrl(String)
* @see #uriBuilderFactory(UriBuilderFactory)
*/
Builder exchangeStrategies(ExchangeStrategies strategies);
Builder defaultUriVariables(Map<String, ?> defaultUriVariables);
/**
* Configure directly an {@link ExchangeFunction} instead of separately
* providing a {@link ClientHttpConnector} and/or
* {@link ExchangeStrategies}.
* @param exchangeFunction the exchange function to use
* Provide a pre-configured {@link UriBuilderFactory} instance. This is
* an alternative to and effectively overrides the following:
* <ul>
* <li>{@link #baseUrl(String)}
* <li>{@link #defaultUriVariables(Map)}.
* </ul>
* @param uriBuilderFactory the URI builder factory to use
* @see #baseUrl(String)
* @see #defaultUriVariables(Map)
*/
Builder exchangeFunction(ExchangeFunction exchangeFunction);
Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory);
/**
* Add the given header to all requests that haven't added it.
@ -212,6 +213,41 @@ public interface WebClient {
*/
Builder defaultCookie(String cookieName, String... cookieValues);
/**
* Configure the {@link ClientHttpConnector} to use.
* <p>By default an instance of
* {@link org.springframework.http.client.reactive.ReactorClientHttpConnector
* ReactorClientHttpConnector} is created if this is not set. However a
* shared instance may be passed instead, e.g. for use with multiple
* {@code WebClient}'s targeting different base URIs.
* @param connector the connector to use
* @see #exchangeStrategies(ExchangeStrategies)
* @see #exchangeFunction(ExchangeFunction)
*/
Builder clientConnector(ClientHttpConnector connector);
/**
* Configure the {@link ExchangeStrategies} to use.
* <p>By default {@link ExchangeStrategies#withDefaults()} is used.
* @param strategies the strategies to use
* @see #clientConnector(ClientHttpConnector)
* @see #exchangeFunction(ExchangeFunction)
*/
Builder exchangeStrategies(ExchangeStrategies strategies);
/**
* Provide a pre-configured {@link ExchangeFunction} instance. This is
* an alternative to and effectively overrides the following:
* <ul>
* <li>{@link #clientConnector(ClientHttpConnector)}
* <li>{@link #exchangeStrategies(ExchangeStrategies)}.
* </ul>
* @param exchangeFunction the exchange function to use
* @see #clientConnector(ClientHttpConnector)
* @see #exchangeStrategies(ExchangeStrategies)
*/
Builder exchangeFunction(ExchangeFunction exchangeFunction);
/**
* Builder the {@link WebClient} instance.
*/
@ -234,14 +270,19 @@ public interface WebClient {
* 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(String)
*/
HeaderSpec uri(String uri, Object... uriVariables);
/**
* 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.
*/
HeaderSpec uri(String uri, Map<String, ?> uriVariables);
/**
* Build the URI for the request using the {@link UriBuilderFactory}
* configured for this client.
* @see #builder(String)
*/
HeaderSpec uri(Function<UriBuilderFactory, URI> uriFunction);

View File

@ -99,7 +99,7 @@ public class DefaultWebClientTests {
private WebClient.Builder builder() {
return WebClient.builder("/base").exchangeFunction(this.exchangeFunction);
return WebClient.builder().baseUrl("/base").exchangeFunction(this.exchangeFunction);
}
private ClientRequest<?> verifyExchange() {