diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java index 112167d06a..07833effee 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java @@ -34,10 +34,14 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyExtractor; /** - * Represents an HTTP response, as returned by the {@link ExchangeFunction}. - * Access to headers and body is offered by {@link Headers} and - * {@link #body(BodyExtractor)}, {@link #bodyToMono(Class)}, {@link #bodyToFlux(Class)} - * respectively. + * Represents an HTTP response, as returned by {@link WebClient} and also + * {@link ExchangeFunction}. Provides access to the response status and headers, + * and also methods to consume the response body. + * + *

NOTE: When given access to a {@link ClientResponse} you + * must always use the response body or entity methods to ensure resources are + * released and to avoid potential issues with HTTP connection pooling. If not + * interested in the response body, use {@code "bodyToMono(Void.class)"}. * * @author Brian Clozel * @author Arjen Poutsma 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 ae057de0f3..1d048db873 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 @@ -446,48 +446,46 @@ public interface WebClient { S attributes(Consumer> attributesConsumer); /** - * Exchange the request for a {@code ClientResponse} with full access - * to the response status and headers before extracting the body. - *

Use {@link Mono#flatMap(Function)} or - * {@link Mono#flatMapMany(Function)} to compose further on the response: - *

-		 * Mono<Pojo> mono = client.get().uri("/")
+		 * Perform the HTTP request and retrieve the response body:
+		 * 

+		 * Mono<Person> bodyMono = client.get()
+		 *     .uri("/persons/1")
+		 *     .accept(MediaType.APPLICATION_JSON)
+		 *     .retrieve()
+		 *     .bodyToMono(Person.class);
+		 * 
+ *

This method is a shortcut to using {@link #exchange()} and + * decoding the response body through {@link ClientResponse}. + * @return {@code ResponseSpec} to specify how to decode the body + * @see #exchange() + */ + ResponseSpec retrieve(); + + /** + * Perform the HTTP request and return a {@link ClientResponse} with the + * response status and headers. You can then use methods of the response + * to consume the body: + *

+		 * Mono<Person> mono = client.get()
+		 *     .uri("/persons/1")
 		 *     .accept(MediaType.APPLICATION_JSON)
 		 *     .exchange()
-		 *     .flatMap(response -> response.bodyToMono(Pojo.class));
+		 *     .flatMap(response -> response.bodyToMono(Person.class));
 		 *
-		 * Flux<Pojo> flux = client.get().uri("/")
+		 * Flux<Person> flux = client.get()
+		 *     .uri("/persons")
 		 *     .accept(MediaType.APPLICATION_STREAM_JSON)
 		 *     .exchange()
-		 *     .flatMapMany(response -> response.bodyToFlux(Pojo.class));
+		 *     .flatMapMany(response -> response.bodyToFlux(Person.class));
 		 * 
- *

The response body should always be consumed with {@code bodyTo*} - * or {@code toEntity*} methods; if you do not care about the body, - * you can use {@code bodyToMono(Void.class)}. - *

Not consuming the response body might lead to HTTP connection pool - * inconsistencies or memory leaks. - * @return a {@code Mono} with the response + *

NOTE: You must always use of the body or entity + * methods on {@link ClientResponse} to ensure resources are released and + * avoid potential issues with HTTP connection pooling. If not interested + * in the response body, use {@code "bodyToMono(Void.class)"} to complete. + * @return a {@code Mono} for the response * @see #retrieve() */ Mono exchange(); - - /** - * A variant of {@link #exchange()} that provides the shortest path to - * retrieving the full response (i.e. status, headers, and body) where - * instead of returning {@code Mono} it exposes shortcut - * methods to extract the response body. - *

Use of this method is simpler when you don't need to deal directly - * with {@link ClientResponse}, e.g. to use a custom {@code BodyExtractor} - * or to check the status and headers before extracting the response. - *

-		 * Mono<Pojo> bodyMono = client.get().uri("/")
-		 *     .accept(MediaType.APPLICATION_JSON)
-		 *     .retrieve()
-		 *     .bodyToMono(Pojo.class);
-		 * 
- * @return spec with options for extracting the response body - */ - ResponseSpec retrieve(); } diff --git a/src/docs/asciidoc/web/webflux-webclient.adoc b/src/docs/asciidoc/web/webflux-webclient.adoc index 9b2db5da50..abe5a77c62 100644 --- a/src/docs/asciidoc/web/webflux-webclient.adoc +++ b/src/docs/asciidoc/web/webflux-webclient.adoc @@ -88,7 +88,7 @@ At this level you can also create a full `ResponseEntity`: Mono> result = client.get() .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON) .exchange() - .flatMap(response -> response.bodyToEntity(Person.class)); + .flatMap(response -> response.toEntity(Person.class)); ---- Note that unlike `retrieve()`, with `exchange()` there are no automatic error signals for @@ -96,13 +96,10 @@ Note that unlike `retrieve()`, with `exchange()` there are no automatic error si [CAUTION] ==== -When you use `exchange()`, you must call `response.close()` if you do not intend to read -the response body in order to close the underlying HTTP connection. Not doing so can -result in connection pool inconsistencies or memory leaks. - -You do not have to call `response.close()` if you consume the body because forcing a -connection to be closed negates the benefits of persistent connections and connection -pooling. +When using `exchange()` you must always use any of the body or entity methods of +`ClientResponse` to ensure resources are released and to avoid potential issues with HTTP +connection pooling. If not interested in the response body use `bodyToMono(Void.class)` +to complete. ====