4 changed files with 641 additions and 81 deletions
@ -0,0 +1,227 @@ |
|||||||
|
/* |
||||||
|
* 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.jetbrains.annotations.NotNull; |
||||||
|
import org.reactivestreams.Publisher; |
||||||
|
import reactor.core.publisher.Mono; |
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders; |
||||||
|
import org.springframework.http.HttpMethod; |
||||||
|
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.DefaultUriBuilderFactory; |
||||||
|
import org.springframework.web.util.UriBuilderFactory; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Default implementation of {@link WebClientOperations}. |
||||||
|
* |
||||||
|
* @author Rossen Stoyanchev |
||||||
|
* @since 5.0 |
||||||
|
*/ |
||||||
|
class DefaultWebClientOperations implements WebClientOperations { |
||||||
|
|
||||||
|
private final WebClient webClient; |
||||||
|
|
||||||
|
private final UriBuilderFactory uriBuilderFactory; |
||||||
|
|
||||||
|
|
||||||
|
DefaultWebClientOperations(WebClient webClient, UriBuilderFactory factory) { |
||||||
|
this.webClient = webClient; |
||||||
|
this.uriBuilderFactory = (factory != null ? factory : new DefaultUriBuilderFactory()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private WebClient getWebClient() { |
||||||
|
return this.webClient; |
||||||
|
} |
||||||
|
|
||||||
|
private UriBuilderFactory getUriBuilderFactory() { |
||||||
|
return this.uriBuilderFactory; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public UriSpec get() { |
||||||
|
return method(HttpMethod.GET); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public UriSpec head() { |
||||||
|
return method(HttpMethod.HEAD); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public UriSpec post() { |
||||||
|
return method(HttpMethod.POST); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public UriSpec put() { |
||||||
|
return method(HttpMethod.PUT); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public UriSpec patch() { |
||||||
|
return method(HttpMethod.PATCH); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public UriSpec delete() { |
||||||
|
return method(HttpMethod.DELETE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public UriSpec options() { |
||||||
|
return method(HttpMethod.OPTIONS); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private UriSpec method(HttpMethod httpMethod) { |
||||||
|
return new DefaultUriSpec(httpMethod); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public WebClientOperations filter(ExchangeFilterFunction filterFunction) { |
||||||
|
WebClient filteredWebClient = this.webClient.filter(filterFunction); |
||||||
|
return new DefaultWebClientOperations(filteredWebClient, this.uriBuilderFactory); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private class DefaultUriSpec implements UriSpec { |
||||||
|
|
||||||
|
private final HttpMethod httpMethod; |
||||||
|
|
||||||
|
|
||||||
|
DefaultUriSpec(HttpMethod httpMethod) { |
||||||
|
this.httpMethod = httpMethod; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public HeaderSpec uri(URI uri) { |
||||||
|
return new DefaultHeaderSpec(ClientRequest.method(this.httpMethod, uri)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public HeaderSpec uri(String uriTemplate, Object... uriVariables) { |
||||||
|
return uri(getUriBuilderFactory().expand(uriTemplate)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public HeaderSpec uri(Function<UriBuilderFactory, URI> uriFunction) { |
||||||
|
return uri(uriFunction.apply(getUriBuilderFactory())); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private class DefaultHeaderSpec implements HeaderSpec { |
||||||
|
|
||||||
|
private ClientRequest.BodyBuilder requestBuilder; |
||||||
|
|
||||||
|
|
||||||
|
DefaultHeaderSpec(ClientRequest.BodyBuilder requestBuilder) { |
||||||
|
this.requestBuilder = requestBuilder; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultHeaderSpec header(String headerName, String... headerValues) { |
||||||
|
this.requestBuilder.header(headerName, headerValues); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultHeaderSpec headers(HttpHeaders headers) { |
||||||
|
this.requestBuilder.headers(headers); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultHeaderSpec accept(MediaType... acceptableMediaTypes) { |
||||||
|
this.requestBuilder.accept(acceptableMediaTypes); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultHeaderSpec acceptCharset(Charset... acceptableCharsets) { |
||||||
|
this.requestBuilder.acceptCharset(acceptableCharsets); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultHeaderSpec contentType(MediaType contentType) { |
||||||
|
this.requestBuilder.contentType(contentType); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultHeaderSpec contentLength(long contentLength) { |
||||||
|
this.requestBuilder.contentLength(contentLength); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultHeaderSpec cookie(String name, String value) { |
||||||
|
this.requestBuilder.cookie(name, value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultHeaderSpec cookies(MultiValueMap<String, String> cookies) { |
||||||
|
this.requestBuilder.cookies(cookies); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultHeaderSpec ifModifiedSince(ZonedDateTime ifModifiedSince) { |
||||||
|
this.requestBuilder.ifModifiedSince(ifModifiedSince); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultHeaderSpec ifNoneMatch(String... ifNoneMatches) { |
||||||
|
this.requestBuilder.ifNoneMatch(ifNoneMatches); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Mono<ClientResponse> exchange() { |
||||||
|
ClientRequest<Void> request = this.requestBuilder.build(); |
||||||
|
return getWebClient().exchange(request); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public <T> Mono<ClientResponse> exchange(BodyInserter<T, ? super ClientHttpRequest> inserter) { |
||||||
|
ClientRequest<T> request = this.requestBuilder.body(inserter); |
||||||
|
return getWebClient().exchange(request); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public <T, S extends Publisher<T>> Mono<ClientResponse> exchange(S publisher, Class<T> elementClass) { |
||||||
|
ClientRequest<S> request = this.requestBuilder.body(publisher, elementClass); |
||||||
|
return getWebClient().exchange(request); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,51 @@ |
|||||||
|
/* |
||||||
|
* 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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,295 @@ |
|||||||
|
/* |
||||||
|
* 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); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue