From 3232cb626094123ff69ad4053b0a92f8cc768dad Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Mon, 3 Jul 2017 15:55:58 +0200 Subject: [PATCH] Avoid required uri when using WebClient w/ base url This commit makes the `uri` step of the WebClient optional, so that users who have specified a base URL during WebClient config do not need to provide an empty one (i.e. `url("")`). The basic idea of this fix is that the HTTP method methods in WebClient (`get`, `post`, etc.) should be able to "bypass" the uri stage, and skip straight to defining headers, or even doing an exchange or retrieve (i.e. call methods on `RequestHeaderSpec` or `RequestBodySpec`). I have accomplished this by adding two new composed interfaces: `RequestHeadersUriSpec` and `RequestBodyUriSpec`. `RequestHeadersUriSpec` extends from the existing `UriSpec` and `RequestHeaderSpec`, while `RequestBodyUriSpec` extends from `UriSpec` and `RequestBodySpec`. These types are returned from the HTTP methods (`get`, `post` etc). The `uri` methods on these types return a plain `RequestHeaderSpec` and `RequestBodySpec` (i.e. types without the `uri` methods), so that you can call `uri` once only. Issue: SPR-15695 --- .../function/client/DefaultWebClient.java | 103 ++++++++---------- .../reactive/function/client/WebClient.java | 26 +++-- 2 files changed, 65 insertions(+), 64 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java index 9c2c85d56e6..6ef4b089a95 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java @@ -86,48 +86,48 @@ class DefaultWebClient implements WebClient { @Override - public UriSpec> get() { + public RequestHeadersUriSpec get() { return methodInternal(HttpMethod.GET); } @Override - public UriSpec> head() { + public RequestHeadersUriSpec head() { return methodInternal(HttpMethod.HEAD); } @Override - public UriSpec post() { + public RequestBodyUriSpec post() { return methodInternal(HttpMethod.POST); } @Override - public UriSpec put() { + public RequestBodyUriSpec put() { return methodInternal(HttpMethod.PUT); } @Override - public UriSpec patch() { + public RequestBodyUriSpec patch() { return methodInternal(HttpMethod.PATCH); } @Override - public UriSpec> delete() { + public RequestHeadersUriSpec delete() { return methodInternal(HttpMethod.DELETE); } @Override - public UriSpec> options() { + public RequestHeadersUriSpec options() { return methodInternal(HttpMethod.OPTIONS); } @Override - public UriSpec method(HttpMethod httpMethod) { + public RequestBodyUriSpec method(HttpMethod httpMethod) { return methodInternal(httpMethod); } @SuppressWarnings("unchecked") - private > UriSpec methodInternal(HttpMethod httpMethod) { - return new DefaultUriSpec<>(httpMethod); + private RequestBodyUriSpec methodInternal(HttpMethod httpMethod) { + return new DefaultRequestBodyUriSpec(httpMethod); } @Override @@ -135,43 +135,13 @@ class DefaultWebClient implements WebClient { return this.builder; } - private class DefaultUriSpec> implements UriSpec { + + private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec { private final HttpMethod httpMethod; - - DefaultUriSpec(HttpMethod httpMethod) { - this.httpMethod = httpMethod; - } - - @Override - public S uri(String uriTemplate, Object... uriVariables) { - return uri(uriBuilderFactory.expand(uriTemplate, uriVariables)); - } - - @Override - public S uri(String uriTemplate, Map uriVariables) { - return uri(uriBuilderFactory.expand(uriTemplate, uriVariables)); - } - - @Override - public S uri(Function uriFunction) { - return uri(uriFunction.apply(uriBuilderFactory.builder())); - } - - @Override - @SuppressWarnings("unchecked") - public S uri(URI uri) { - return (S) new DefaultRequestBodySpec(this.httpMethod, uri); - } - } - - - private class DefaultRequestBodySpec implements RequestBodySpec { - - private final HttpMethod httpMethod; - - private final URI uri; + @Nullable + private URI uri; @Nullable private HttpHeaders headers; @@ -185,9 +155,29 @@ class DefaultWebClient implements WebClient { @Nullable private Map attributes; - DefaultRequestBodySpec(HttpMethod httpMethod, URI uri) { + DefaultRequestBodyUriSpec(HttpMethod httpMethod) { this.httpMethod = httpMethod; + } + + @Override + public RequestBodySpec uri(String uriTemplate, Object... uriVariables) { + return uri(uriBuilderFactory.expand(uriTemplate, uriVariables)); + } + + @Override + public RequestBodySpec uri(String uriTemplate, Map uriVariables) { + return uri(uriBuilderFactory.expand(uriTemplate, uriVariables)); + } + + @Override + public RequestBodySpec uri(Function uriFunction) { + return uri(uriFunction.apply(uriBuilderFactory.builder())); + } + + @Override + public RequestBodySpec uri(URI uri) { this.uri = uri; + return this; } private HttpHeaders getHeaders() { @@ -212,7 +202,7 @@ class DefaultWebClient implements WebClient { } @Override - public DefaultRequestBodySpec header(String headerName, String... headerValues) { + public DefaultRequestBodyUriSpec header(String headerName, String... headerValues) { for (String headerValue : headerValues) { getHeaders().add(headerName, headerValue); } @@ -220,7 +210,7 @@ class DefaultWebClient implements WebClient { } @Override - public DefaultRequestBodySpec headers(Consumer headersConsumer) { + public DefaultRequestBodyUriSpec headers(Consumer headersConsumer) { Assert.notNull(headersConsumer, "'headersConsumer' must not be null"); headersConsumer.accept(getHeaders()); return this; @@ -240,37 +230,37 @@ class DefaultWebClient implements WebClient { } @Override - public DefaultRequestBodySpec accept(MediaType... acceptableMediaTypes) { + public DefaultRequestBodyUriSpec accept(MediaType... acceptableMediaTypes) { getHeaders().setAccept(Arrays.asList(acceptableMediaTypes)); return this; } @Override - public DefaultRequestBodySpec acceptCharset(Charset... acceptableCharsets) { + public DefaultRequestBodyUriSpec acceptCharset(Charset... acceptableCharsets) { getHeaders().setAcceptCharset(Arrays.asList(acceptableCharsets)); return this; } @Override - public DefaultRequestBodySpec contentType(MediaType contentType) { + public DefaultRequestBodyUriSpec contentType(MediaType contentType) { getHeaders().setContentType(contentType); return this; } @Override - public DefaultRequestBodySpec contentLength(long contentLength) { + public DefaultRequestBodyUriSpec contentLength(long contentLength) { getHeaders().setContentLength(contentLength); return this; } @Override - public DefaultRequestBodySpec cookie(String name, String value) { + public DefaultRequestBodyUriSpec cookie(String name, String value) { getCookies().add(name, value); return this; } @Override - public DefaultRequestBodySpec cookies( + public DefaultRequestBodyUriSpec cookies( Consumer> cookiesConsumer) { Assert.notNull(cookiesConsumer, "'cookiesConsumer' must not be null"); cookiesConsumer.accept(this.cookies); @@ -278,7 +268,7 @@ class DefaultWebClient implements WebClient { } @Override - public DefaultRequestBodySpec ifModifiedSince(ZonedDateTime ifModifiedSince) { + public DefaultRequestBodyUriSpec ifModifiedSince(ZonedDateTime ifModifiedSince) { ZonedDateTime gmt = ifModifiedSince.withZoneSameInstant(ZoneId.of("GMT")); String headerValue = DateTimeFormatter.RFC_1123_DATE_TIME.format(gmt); getHeaders().set(HttpHeaders.IF_MODIFIED_SINCE, headerValue); @@ -286,7 +276,7 @@ class DefaultWebClient implements WebClient { } @Override - public DefaultRequestBodySpec ifNoneMatch(String... ifNoneMatches) { + public DefaultRequestBodyUriSpec ifNoneMatch(String... ifNoneMatches) { getHeaders().setIfNoneMatch(Arrays.asList(ifNoneMatches)); return this; } @@ -320,7 +310,8 @@ class DefaultWebClient implements WebClient { } private ClientRequest.Builder initRequestBuilder() { - return ClientRequest.method(this.httpMethod, this.uri) + URI uri = this.uri != null ? this.uri : uriBuilderFactory.expand(""); + return ClientRequest.method(this.httpMethod, uri) .headers(headers -> headers.addAll(initHeaders())) .cookies(cookies -> cookies.addAll(initCookies())) .attributes(attributes -> attributes.putAll(getAttributes())); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java index ab75b069d5b..c38786f8108 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java @@ -63,49 +63,49 @@ public interface WebClient { * Prepare an HTTP GET request. * @return a spec for specifying the target URL */ - UriSpec> get(); + RequestHeadersUriSpec get(); /** * Prepare an HTTP HEAD request. * @return a spec for specifying the target URL */ - UriSpec> head(); + RequestHeadersUriSpec head(); /** * Prepare an HTTP POST request. * @return a spec for specifying the target URL */ - UriSpec post(); + RequestBodyUriSpec post(); /** * Prepare an HTTP PUT request. * @return a spec for specifying the target URL */ - UriSpec put(); + RequestBodyUriSpec put(); /** * Prepare an HTTP PATCH request. * @return a spec for specifying the target URL */ - UriSpec patch(); + RequestBodyUriSpec patch(); /** * Prepare an HTTP DELETE request. * @return a spec for specifying the target URL */ - UriSpec> delete(); + RequestHeadersUriSpec delete(); /** * Prepare an HTTP OPTIONS request. * @return a spec for specifying the target URL */ - UriSpec> options(); + RequestHeadersUriSpec options(); /** * Prepare a request for the specified {@code HttpMethod}. * @return a spec for specifying the target URL */ - UriSpec method(HttpMethod method); + RequestBodyUriSpec method(HttpMethod method); /** @@ -579,4 +579,14 @@ public interface WebClient { } + + interface RequestHeadersUriSpec> + extends UriSpec, RequestHeadersSpec { + } + + + interface RequestBodyUriSpec extends RequestBodySpec, RequestHeadersUriSpec { + } + + }