Add defaultRequest option to WebClient.Builder

Issue: SPR-16873
This commit is contained in:
Rossen Stoyanchev
2018-07-03 12:43:00 -04:00
parent da5b705328
commit 8bffb6a798
4 changed files with 59 additions and 4 deletions

View File

@@ -79,17 +79,21 @@ class DefaultWebClient implements WebClient {
@Nullable
private final MultiValueMap<String, String> defaultCookies;
@Nullable
private final Consumer<RequestHeadersSpec<?>> defaultRequest;
private final DefaultWebClientBuilder builder;
DefaultWebClient(ExchangeFunction exchangeFunction, @Nullable UriBuilderFactory factory,
@Nullable HttpHeaders defaultHeaders, @Nullable MultiValueMap<String, String> defaultCookies,
DefaultWebClientBuilder builder) {
@Nullable Consumer<RequestHeadersSpec<?>> defaultRequest, DefaultWebClientBuilder builder) {
this.exchangeFunction = exchangeFunction;
this.uriBuilderFactory = (factory != null ? factory : new DefaultUriBuilderFactory());
this.defaultHeaders = defaultHeaders;
this.defaultCookies = defaultCookies;
this.defaultRequest = defaultRequest;
this.builder = builder;
}
@@ -317,6 +321,9 @@ class DefaultWebClient implements WebClient {
}
private ClientRequest.Builder initRequestBuilder() {
if (defaultRequest != null) {
defaultRequest.accept(this);
}
URI uri = (this.uri != null ? this.uri : uriBuilderFactory.expand(""));
return ClientRequest.create(this.httpMethod, uri)
.headers(headers -> headers.addAll(initHeaders()))

View File

@@ -57,6 +57,9 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
@Nullable
private MultiValueMap<String, String> defaultCookies;
@Nullable
private Consumer<WebClient.RequestHeadersSpec<?>> defaultRequest;
@Nullable
private List<ExchangeFilterFunction> filters;
@@ -89,6 +92,7 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
}
this.defaultCookies =
other.defaultCookies != null ? new LinkedMultiValueMap<>(other.defaultCookies) : null;
this.defaultRequest = other.defaultRequest;
this.filters = other.filters != null ? new ArrayList<>(other.filters) : null;
this.connector = other.connector;
this.exchangeFunction = other.exchangeFunction;
@@ -152,6 +156,13 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
return this.defaultCookies;
}
@Override
public WebClient.Builder defaultRequest(Consumer<WebClient.RequestHeadersSpec<?>> defaultRequest) {
this.defaultRequest = this.defaultRequest != null ?
this.defaultRequest.andThen(defaultRequest) : defaultRequest;
return this;
}
@Override
public WebClient.Builder clientConnector(ClientHttpConnector connector) {
this.connector = connector;
@@ -201,7 +212,7 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
return new DefaultWebClient(filteredExchange, initUriBuilderFactory(),
this.defaultHeaders != null ? unmodifiableCopy(this.defaultHeaders) : null,
this.defaultCookies != null ? unmodifiableCopy(this.defaultCookies) : null,
new DefaultWebClientBuilder(this));
this.defaultRequest, new DefaultWebClientBuilder(this));
}
private ExchangeFunction initExchangeFunction() {

View File

@@ -226,7 +226,7 @@ public interface WebClient {
Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory);
/**
* A global option to specify a header to be added to every request,
* Global option to specify a header to be added to every request,
* if the request does not already contain such a header.
* @param header the header name
* @param values the header values
@@ -241,7 +241,7 @@ public interface WebClient {
Builder defaultHeaders(Consumer<HttpHeaders> headersConsumer);
/**
* A global option to specify a cookie to be added to every request,
* Global option to specify a cookie to be added to every request,
* if the request does not already contain such a cookie.
* @param cookie the cookie name
* @param values the cookie values
@@ -255,6 +255,14 @@ public interface WebClient {
*/
Builder defaultCookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);
/**
* Provide a consumer to modify every request being built just before the
* call to {@link RequestHeadersSpec#exchange() exchange()}.
* @param defaultRequest the consumer to use for modifying requests
* @since 5.1
*/
Builder defaultRequest(Consumer<RequestHeadersSpec<?>> defaultRequest);
/**
* Add the given filter to the filter chain.
* @param filter the filter to be added to the chain

View File

@@ -30,6 +30,8 @@ import org.mockito.MockitoAnnotations;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.NamedInheritableThreadLocal;
import org.springframework.core.NamedThreadLocal;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -138,6 +140,33 @@ public class DefaultWebClientTests {
verifyNoMoreInteractions(this.exchangeFunction);
}
@Test
public void defaultRequest() {
ThreadLocal<String> context = new NamedThreadLocal<>("foo");
Map<String, Object> actual = new HashMap<>();
ExchangeFilterFunction filter = (request, next) -> {
actual.putAll(request.attributes());
return next.exchange(request);
};
WebClient client = this.builder
.defaultRequest(spec -> spec.attribute("foo", context.get()))
.filter(filter)
.build();
try {
context.set("bar");
client.get().uri("/path").attribute("foo", "bar").exchange();
}
finally {
context.remove();
}
assertEquals("bar", actual.get("foo"));
}
@Test(expected = IllegalArgumentException.class)
public void bodyObjectPublisher() {
Mono<Void> mono = Mono.empty();