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
This commit is contained in:
parent
1c07369406
commit
3232cb6260
|
|
@ -86,48 +86,48 @@ class DefaultWebClient implements WebClient {
|
|||
|
||||
|
||||
@Override
|
||||
public UriSpec<RequestHeadersSpec<?>> get() {
|
||||
public RequestHeadersUriSpec<?> get() {
|
||||
return methodInternal(HttpMethod.GET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriSpec<RequestHeadersSpec<?>> head() {
|
||||
public RequestHeadersUriSpec<?> head() {
|
||||
return methodInternal(HttpMethod.HEAD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriSpec<RequestBodySpec> post() {
|
||||
public RequestBodyUriSpec post() {
|
||||
return methodInternal(HttpMethod.POST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriSpec<RequestBodySpec> put() {
|
||||
public RequestBodyUriSpec put() {
|
||||
return methodInternal(HttpMethod.PUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriSpec<RequestBodySpec> patch() {
|
||||
public RequestBodyUriSpec patch() {
|
||||
return methodInternal(HttpMethod.PATCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriSpec<RequestHeadersSpec<?>> delete() {
|
||||
public RequestHeadersUriSpec<?> delete() {
|
||||
return methodInternal(HttpMethod.DELETE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriSpec<RequestHeadersSpec<?>> options() {
|
||||
public RequestHeadersUriSpec<?> options() {
|
||||
return methodInternal(HttpMethod.OPTIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriSpec<RequestBodySpec> method(HttpMethod httpMethod) {
|
||||
public RequestBodyUriSpec method(HttpMethod httpMethod) {
|
||||
return methodInternal(httpMethod);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <S extends RequestHeadersSpec<?>> UriSpec<S> 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<S extends RequestHeadersSpec<?>> implements UriSpec<S> {
|
||||
|
||||
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<String, ?> uriVariables) {
|
||||
return uri(uriBuilderFactory.expand(uriTemplate, uriVariables));
|
||||
}
|
||||
|
||||
@Override
|
||||
public S uri(Function<UriBuilder, URI> 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<String, Object> 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<String, ?> uriVariables) {
|
||||
return uri(uriBuilderFactory.expand(uriTemplate, uriVariables));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBodySpec uri(Function<UriBuilder, URI> 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<HttpHeaders> headersConsumer) {
|
||||
public DefaultRequestBodyUriSpec headers(Consumer<HttpHeaders> 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<MultiValueMap<String, String>> 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()));
|
||||
|
|
|
|||
|
|
@ -63,49 +63,49 @@ public interface WebClient {
|
|||
* Prepare an HTTP GET request.
|
||||
* @return a spec for specifying the target URL
|
||||
*/
|
||||
UriSpec<RequestHeadersSpec<?>> get();
|
||||
RequestHeadersUriSpec<?> get();
|
||||
|
||||
/**
|
||||
* Prepare an HTTP HEAD request.
|
||||
* @return a spec for specifying the target URL
|
||||
*/
|
||||
UriSpec<RequestHeadersSpec<?>> head();
|
||||
RequestHeadersUriSpec<?> head();
|
||||
|
||||
/**
|
||||
* Prepare an HTTP POST request.
|
||||
* @return a spec for specifying the target URL
|
||||
*/
|
||||
UriSpec<RequestBodySpec> post();
|
||||
RequestBodyUriSpec post();
|
||||
|
||||
/**
|
||||
* Prepare an HTTP PUT request.
|
||||
* @return a spec for specifying the target URL
|
||||
*/
|
||||
UriSpec<RequestBodySpec> put();
|
||||
RequestBodyUriSpec put();
|
||||
|
||||
/**
|
||||
* Prepare an HTTP PATCH request.
|
||||
* @return a spec for specifying the target URL
|
||||
*/
|
||||
UriSpec<RequestBodySpec> patch();
|
||||
RequestBodyUriSpec patch();
|
||||
|
||||
/**
|
||||
* Prepare an HTTP DELETE request.
|
||||
* @return a spec for specifying the target URL
|
||||
*/
|
||||
UriSpec<RequestHeadersSpec<?>> delete();
|
||||
RequestHeadersUriSpec<?> delete();
|
||||
|
||||
/**
|
||||
* Prepare an HTTP OPTIONS request.
|
||||
* @return a spec for specifying the target URL
|
||||
*/
|
||||
UriSpec<RequestHeadersSpec<?>> options();
|
||||
RequestHeadersUriSpec<?> options();
|
||||
|
||||
/**
|
||||
* Prepare a request for the specified {@code HttpMethod}.
|
||||
* @return a spec for specifying the target URL
|
||||
*/
|
||||
UriSpec<RequestBodySpec> method(HttpMethod method);
|
||||
RequestBodyUriSpec method(HttpMethod method);
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -579,4 +579,14 @@ public interface WebClient {
|
|||
|
||||
}
|
||||
|
||||
|
||||
interface RequestHeadersUriSpec<S extends RequestHeadersSpec<S>>
|
||||
extends UriSpec<S>, RequestHeadersSpec<S> {
|
||||
}
|
||||
|
||||
|
||||
interface RequestBodyUriSpec extends RequestBodySpec, RequestHeadersUriSpec<RequestBodySpec> {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue