parent
bc869657c8
commit
f5640cbfe0
|
@ -41,14 +41,26 @@ import org.springframework.web.reactive.function.BodyExtractor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an HTTP response, as returned by {@link WebClient} and also
|
* Represents an HTTP response, as returned by {@link WebClient} and also
|
||||||
* {@link ExchangeFunction}. Provides access to the response status and headers,
|
* {@link ExchangeFunction}. Provides access to the response status and
|
||||||
* and also methods to consume the response body.
|
* headers, and also methods to consume the response body.
|
||||||
*
|
*
|
||||||
* <p><strong>NOTE:</strong> When given access to a {@link ClientResponse},
|
* <p><strong>NOTE:</strong> When using a {@link ClientResponse}
|
||||||
* through the {@code WebClient}
|
* through the {@code WebClient}
|
||||||
* {@link WebClient.RequestHeadersSpec#exchange() exchange()} method,
|
* {@link WebClient.RequestHeadersSpec#exchange() exchange()} method,
|
||||||
* you must always use one of the body or toEntity methods to ensure resources
|
* you have to make sure that the body is consumed or released by using
|
||||||
* are released and avoid potential issues with HTTP connection pooling.
|
* one of the following methods:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #body(BodyExtractor)}</li>
|
||||||
|
* <li>{@link #bodyToMono(Class)} or
|
||||||
|
* {@link #bodyToMono(ParameterizedTypeReference)}</li>
|
||||||
|
* <li>{@link #bodyToFlux(Class)} or
|
||||||
|
* {@link #bodyToFlux(ParameterizedTypeReference)}</li>
|
||||||
|
* <li>{@link #toEntity(Class)} or
|
||||||
|
* {@link #toEntity(ParameterizedTypeReference)}</li>
|
||||||
|
* <li>{@link #toEntityList(Class)} or
|
||||||
|
* {@link #toEntityList(ParameterizedTypeReference)}</li>
|
||||||
|
* <li>{@link #releaseBody()}</li>
|
||||||
|
* </ul>
|
||||||
* You can use {@code bodyToMono(Void.class)} if no response content is
|
* You can use {@code bodyToMono(Void.class)} if no response content is
|
||||||
* expected. However keep in mind that if the response does have content, the
|
* expected. However keep in mind that if the response does have content, the
|
||||||
* connection will be closed and will not be placed back in the pool.
|
* connection will be closed and will not be placed back in the pool.
|
||||||
|
@ -132,6 +144,14 @@ public interface ClientResponse {
|
||||||
*/
|
*/
|
||||||
<T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> elementTypeRef);
|
<T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> elementTypeRef);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the body of this response.
|
||||||
|
* @return a completion signal
|
||||||
|
* @since 5.2
|
||||||
|
* @see org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer)
|
||||||
|
*/
|
||||||
|
Mono<Void> releaseBody();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return this response as a delayed {@code ResponseEntity}.
|
* Return this response as a delayed {@code ResponseEntity}.
|
||||||
* @param bodyClass the expected response body type
|
* @param bodyClass the expected response body type
|
||||||
|
|
|
@ -155,6 +155,13 @@ class DefaultClientResponse implements ClientResponse {
|
||||||
return body(BodyExtractors.toFlux(elementTypeRef));
|
return body(BodyExtractors.toFlux(elementTypeRef));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> releaseBody() {
|
||||||
|
return body(BodyExtractors.toDataBuffers())
|
||||||
|
.map(DataBufferUtils::release)
|
||||||
|
.then();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType) {
|
public <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType) {
|
||||||
return WebClientUtils.toEntity(this, bodyToMono(bodyType));
|
return WebClientUtils.toEntity(this, bodyToMono(bodyType));
|
||||||
|
|
|
@ -118,6 +118,11 @@ public class ClientResponseWrapper implements ClientResponse {
|
||||||
return this.delegate.bodyToFlux(elementTypeRef);
|
return this.delegate.bodyToFlux(elementTypeRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> releaseBody() {
|
||||||
|
return this.delegate.releaseBody();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType) {
|
public <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType) {
|
||||||
return this.delegate.toEntity(bodyType);
|
return this.delegate.toEntity(bodyType);
|
||||||
|
|
|
@ -166,6 +166,25 @@ public class WebClientDataBufferAllocatingTests extends AbstractDataBufferAlloca
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedDataBufferAllocatingTest
|
||||||
|
public void releaseBody(String displayName, DataBufferFactory bufferFactory) {
|
||||||
|
super.bufferFactory = bufferFactory;
|
||||||
|
|
||||||
|
this.server.enqueue(new MockResponse()
|
||||||
|
.setResponseCode(200)
|
||||||
|
.setHeader("Content-Type", "text/plain")
|
||||||
|
.setBody("foo bar"));
|
||||||
|
|
||||||
|
Mono<Void> result = this.webClient.get()
|
||||||
|
.exchange()
|
||||||
|
.flatMap(ClientResponse::releaseBody);
|
||||||
|
|
||||||
|
|
||||||
|
StepVerifier.create(result)
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void testOnStatus(Throwable expected,
|
private void testOnStatus(Throwable expected,
|
||||||
Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction) {
|
Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction) {
|
||||||
|
|
Loading…
Reference in New Issue