Skip to content

Commit 922d5d2

Browse files
HaloFourrstoyanchev
authored andcommitted
Add ResponseSpec#toEntityFlux overload with BodyExtractor
See gh-26114
1 parent 05e3f27 commit 922d5d2

File tree

4 files changed

+56
-1
lines changed

4 files changed

+56
-1
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@
4343
import org.springframework.http.MediaType;
4444
import org.springframework.http.ResponseEntity;
4545
import org.springframework.http.client.reactive.ClientHttpRequest;
46+
import org.springframework.http.client.reactive.ClientHttpResponse;
4647
import org.springframework.lang.Nullable;
4748
import org.springframework.util.Assert;
4849
import org.springframework.util.CollectionUtils;
4950
import org.springframework.util.LinkedMultiValueMap;
5051
import org.springframework.util.MultiValueMap;
52+
import org.springframework.web.reactive.function.BodyExtractor;
5153
import org.springframework.web.reactive.function.BodyInserter;
5254
import org.springframework.web.reactive.function.BodyInserters;
5355
import org.springframework.web.util.UriBuilder;
@@ -599,6 +601,12 @@ public <T> Mono<ResponseEntity<Flux<T>>> toEntityFlux(ParameterizedTypeReference
599601
handlerEntityFlux(response, response.bodyToFlux(elementTypeRef)));
600602
}
601603

604+
@Override
605+
public <T> Mono<ResponseEntity<Flux<T>>> toEntityFlux(BodyExtractor<Flux<T>, ? super ClientHttpResponse> bodyExtractor) {
606+
return this.responseMono.flatMap(response ->
607+
handlerEntityFlux(response, response.body(bodyExtractor)));
608+
}
609+
602610
@Override
603611
public Mono<ResponseEntity<Void>> toBodilessEntity() {
604612
return this.responseMono.flatMap(response ->

spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@
4040
import org.springframework.http.ResponseEntity;
4141
import org.springframework.http.client.reactive.ClientHttpConnector;
4242
import org.springframework.http.client.reactive.ClientHttpRequest;
43+
import org.springframework.http.client.reactive.ClientHttpResponse;
4344
import org.springframework.http.codec.ClientCodecConfigurer;
4445
import org.springframework.util.MultiValueMap;
46+
import org.springframework.web.reactive.function.BodyExtractor;
4547
import org.springframework.web.reactive.function.BodyInserter;
4648
import org.springframework.web.reactive.function.BodyInserters;
4749
import org.springframework.web.util.DefaultUriBuilderFactory;
@@ -889,14 +891,23 @@ ResponseSpec onRawStatus(IntPredicate statusCodePredicate,
889891
<T> Mono<ResponseEntity<Flux<T>>> toEntityFlux(Class<T> elementType);
890892

891893
/**
892-
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
894+
* Variant of {@link #toEntityFlux(Class)} with a {@link ParameterizedTypeReference}.
893895
* @param elementTypeReference the type of element to decode the target Flux to
894896
* @param <T> the body element type
895897
* @return the {@code ResponseEntity}
896898
* @since 5.3.1
897899
*/
898900
<T> Mono<ResponseEntity<Flux<T>>> toEntityFlux(ParameterizedTypeReference<T> elementTypeReference);
899901

902+
/**
903+
* Variant of {@link #toEntityFlux(Class)} with a {@link BodyExtractor}.
904+
* @param bodyExtractor the {@code BodyExtractor} that reads from the response
905+
* @param <T> the body element type
906+
* @return the {@code ResponseEntity}
907+
* @since 5.3.2
908+
*/
909+
<T> Mono<ResponseEntity<Flux<T>>> toEntityFlux(BodyExtractor<Flux<T>, ? super ClientHttpResponse> bodyExtractor);
910+
900911
/**
901912
* Return a {@code ResponseEntity} without a body. For an error response
902913
* (status code of 4xx or 5xx), the {@code Mono} emits a

spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.mockito.junit.jupiter.MockitoSettings;
3232
import org.mockito.quality.Strictness;
3333
import org.reactivestreams.Publisher;
34+
import org.springframework.web.reactive.function.BodyExtractors;
3435
import reactor.core.publisher.Mono;
3536
import reactor.test.StepVerifier;
3637

@@ -459,6 +460,7 @@ public void onStatusHandlersApplyForToEntityMethods() {
459460
testStatusHandlerForToEntity(spec.toEntityList(new ParameterizedTypeReference<String>() {}));
460461
testStatusHandlerForToEntity(spec.toEntityFlux(String.class));
461462
testStatusHandlerForToEntity(spec.toEntityFlux(new ParameterizedTypeReference<String>() {}));
463+
testStatusHandlerForToEntity(spec.toEntityFlux(BodyExtractors.toFlux(String.class)));
462464
}
463465

464466
private void testStatusHandlerForToEntity(Publisher<?> responsePublisher) {

spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.junit.jupiter.api.Test;
4242
import org.junit.jupiter.params.ParameterizedTest;
4343
import org.junit.jupiter.params.provider.MethodSource;
44+
import org.springframework.web.reactive.function.BodyExtractors;
4445
import reactor.core.publisher.Flux;
4546
import reactor.core.publisher.Mono;
4647
import reactor.netty.http.client.HttpClient;
@@ -342,6 +343,39 @@ void retrieveJsonArrayAsResponseEntityFlux(ClientHttpConnector connector) {
342343
});
343344
}
344345

346+
@ParameterizedWebClientTest
347+
void retrieveJsonArrayAsResponseEntityFluxWithBodyExtractor(ClientHttpConnector connector) {
348+
startServer(connector);
349+
350+
String content = "[{\"bar\":\"bar1\",\"foo\":\"foo1\"}, {\"bar\":\"bar2\",\"foo\":\"foo2\"}]";
351+
prepareResponse(response -> response
352+
.setHeader("Content-Type", "application/json").setBody(content));
353+
354+
ResponseEntity<Flux<Pojo>> entity = this.webClient.get()
355+
.uri("/json").accept(MediaType.APPLICATION_JSON)
356+
.retrieve()
357+
.toEntityFlux(BodyExtractors.toFlux(Pojo.class))
358+
.block(Duration.ofSeconds(3));
359+
360+
assertThat(entity).isNotNull();
361+
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
362+
assertThat(entity.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON);
363+
assertThat(entity.getHeaders().getContentLength()).isEqualTo(58);
364+
365+
assertThat(entity.getBody()).isNotNull();
366+
StepVerifier.create(entity.getBody())
367+
.expectNext(new Pojo("foo1", "bar1"))
368+
.expectNext(new Pojo("foo2", "bar2"))
369+
.expectComplete()
370+
.verify(Duration.ofSeconds(3));
371+
372+
expectRequestCount(1);
373+
expectRequest(request -> {
374+
assertThat(request.getPath()).isEqualTo("/json");
375+
assertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo("application/json");
376+
});
377+
}
378+
345379
@Test // gh-24788
346380
void retrieveJsonArrayAsBodilessEntityShouldReleasesConnection() {
347381

0 commit comments

Comments
 (0)