diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java index 1ecc3858af9..db8b511927c 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java @@ -534,25 +534,29 @@ class DefaultWebClient implements WebClient { @Override public Mono bodyToMono(Class elementClass) { Assert.notNull(elementClass, "Class must not be null"); - return this.responseMono.flatMap(response -> handleBodyMono(response, response.bodyToMono(elementClass))); + return this.responseMono.flatMap(response -> + handleBodyMono(response, response.bodyToMono(elementClass))); } @Override public Mono bodyToMono(ParameterizedTypeReference elementTypeRef) { Assert.notNull(elementTypeRef, "ParameterizedTypeReference must not be null"); - return this.responseMono.flatMap(response -> handleBodyMono(response, response.bodyToMono(elementTypeRef))); + return this.responseMono.flatMap(response -> + handleBodyMono(response, response.bodyToMono(elementTypeRef))); } @Override public Flux bodyToFlux(Class elementClass) { Assert.notNull(elementClass, "Class must not be null"); - return this.responseMono.flatMapMany(response -> handleBodyFlux(response, response.bodyToFlux(elementClass))); + return this.responseMono.flatMapMany(response -> + handleBodyFlux(response, response.bodyToFlux(elementClass))); } @Override public Flux bodyToFlux(ParameterizedTypeReference elementTypeRef) { Assert.notNull(elementTypeRef, "ParameterizedTypeReference must not be null"); - return this.responseMono.flatMapMany(response -> handleBodyFlux(response, response.bodyToFlux(elementTypeRef))); + return this.responseMono.flatMapMany(response -> + handleBodyFlux(response, response.bodyToFlux(elementTypeRef))); } @Override @@ -585,18 +589,14 @@ class DefaultWebClient implements WebClient { @Override public Mono>> toEntityFlux(Class elementType) { - return this.responseMono.map(response -> - ResponseEntity.status(response.rawStatusCode()) - .headers(response.headers().asHttpHeaders()) - .body(response.bodyToFlux(elementType))); + return this.responseMono.flatMap(response -> + handlerEntityFlux(response, response.bodyToFlux(elementType))); } @Override - public Mono>> toEntityFlux(ParameterizedTypeReference elementTypeReference) { - return this.responseMono.map(response -> - ResponseEntity.status(response.rawStatusCode()) - .headers(response.headers().asHttpHeaders()) - .body(response.bodyToFlux(elementTypeReference))); + public Mono>> toEntityFlux(ParameterizedTypeReference elementTypeRef) { + return this.responseMono.flatMap(response -> + handlerEntityFlux(response, response.bodyToFlux(elementTypeRef))); } @Override @@ -619,6 +619,16 @@ class DefaultWebClient implements WebClient { return (result != null ? result.flux().switchIfEmpty(body) : body); } + private Mono>> handlerEntityFlux(ClientResponse response, Flux body) { + ResponseEntity> entity = new ResponseEntity<>( + body.onErrorResume(WebClientUtils.WRAP_EXCEPTION_PREDICATE, exceptionWrappingFunction(response)), + response.headers().asHttpHeaders(), + response.rawStatusCode()); + + Mono>> result = applyStatusHandlers(response); + return (result != null ? result.defaultIfEmpty(entity) : Mono.just(entity)); + } + private Function> exceptionWrappingFunction(ClientResponse response) { return t -> response.createException().flatMap(ex -> Mono.error(ex.initCause(t))); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java index 8e2a058fd38..ebc6f823001 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java @@ -30,10 +30,12 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import org.springframework.core.NamedThreadLocal; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -443,6 +445,27 @@ public class DefaultWebClientTests { verify(predicate2).test(HttpStatus.BAD_REQUEST); } + @Test // gh-26069 + public void onStatusHandlersApplyForToEntityMethods() { + + ClientResponse response = ClientResponse.create(HttpStatus.BAD_REQUEST).build(); + given(exchangeFunction.exchange(any())).willReturn(Mono.just(response)); + + WebClient.ResponseSpec spec = this.builder.build().get().uri("/path").retrieve(); + + testStatusHandlerForToEntity(spec.toEntity(String.class)); + testStatusHandlerForToEntity(spec.toEntity(new ParameterizedTypeReference() {})); + testStatusHandlerForToEntity(spec.toEntityList(String.class)); + testStatusHandlerForToEntity(spec.toEntityList(new ParameterizedTypeReference() {})); + testStatusHandlerForToEntity(spec.toEntityFlux(String.class)); + testStatusHandlerForToEntity(spec.toEntityFlux(new ParameterizedTypeReference() {})); + } + + private void testStatusHandlerForToEntity(Publisher responsePublisher) { + StepVerifier.create(responsePublisher).expectError(WebClientResponseException.class).verify(); + } + + private ClientRequest verifyAndGetRequest() { ClientRequest request = this.captor.getValue(); verify(this.exchangeFunction).exchange(request);