From 7a516170ef7fbaf6433adfd355baf7f6a96ce924 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Thu, 7 Sep 2023 15:14:03 +0100 Subject: [PATCH] WebClientResponseException supports decoding empty content Closes gh-31179 --- .../client/DefaultClientResponse.java | 6 +++- .../client/WebClientResponseException.java | 11 +++--- .../client/DefaultClientResponseTests.java | 36 +++++++++++++++++++ 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java index 53f7e7ca8050..6b4e35a7138a 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -50,6 +50,7 @@ import org.springframework.util.Assert; import org.springframework.util.MimeType; import org.springframework.util.MultiValueMap; +import org.springframework.util.ObjectUtils; import org.springframework.web.reactive.function.BodyExtractor; import org.springframework.web.reactive.function.BodyExtractors; @@ -233,6 +234,9 @@ public Mono createException() { private Function initDecodeFunction(byte[] body, @Nullable MediaType contentType) { return targetType -> { + if (ObjectUtils.isEmpty(body)) { + return null; + } Decoder decoder = null; for (HttpMessageReader reader : strategies().messageReaders()) { if (reader.canRead(targetType, contentType)) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java index d08ed1ab588d..057cc0b09562 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -233,22 +233,21 @@ public String getResponseBodyAsString(Charset defaultCharset) { */ @Nullable public E getResponseBodyAs(Class targetType) { - return getResponseBodyAs(ResolvableType.forClass(targetType)); + return decodeBody(ResolvableType.forClass(targetType)); } /** - * Variant of {@link #getResponseBodyAs(Class)} with - * {@link ParameterizedTypeReference}. + * Variant of {@link #getResponseBodyAs(Class)} with {@link ParameterizedTypeReference}. * @since 6.0 */ @Nullable public E getResponseBodyAs(ParameterizedTypeReference targetType) { - return getResponseBodyAs(ResolvableType.forType(targetType.getType())); + return decodeBody(ResolvableType.forType(targetType.getType())); } @SuppressWarnings("unchecked") @Nullable - private E getResponseBodyAs(ResolvableType targetType) { + private E decodeBody(ResolvableType targetType) { Assert.state(this.bodyDecodeFunction != null, "Decoder function not set"); return (E) this.bodyDecodeFunction.apply(targetType); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java index 1885b66d4e00..97f0253796f7 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java @@ -21,6 +21,7 @@ import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.OptionalLong; import org.junit.jupiter.api.BeforeEach; @@ -45,6 +46,7 @@ import org.springframework.http.client.reactive.ClientHttpResponse; import org.springframework.http.codec.DecoderHttpMessageReader; import org.springframework.http.codec.HttpMessageReader; +import org.springframework.http.codec.json.Jackson2JsonDecoder; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -346,6 +348,40 @@ void createException() { assertThat(exception.getResponseBodyAsByteArray()).isEqualTo(bytes); } + @Test + void createExceptionAndDecodeContent() { + byte[] bytes = "{\"name\":\"Jason\"}".getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = DefaultDataBufferFactory.sharedInstance.wrap(bytes); + + httpHeaders.setContentType(MediaType.APPLICATION_JSON); + given(mockResponse.getStatusCode()).willReturn(HttpStatus.NOT_FOUND); + given(mockResponse.getBody()).willReturn(Flux.just(buffer)); + + given(mockExchangeStrategies.messageReaders()).willReturn(List.of( + new DecoderHttpMessageReader<>(new ByteArrayDecoder()), + new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()))); + + WebClientResponseException ex = defaultClientResponse.createException().block(); + assertThat(ex.getResponseBodyAs(Map.class)).hasSize(1).containsEntry("name", "Jason"); + } + + @Test + void createExceptionAndDecodeWithoutContent() { + byte[] bytes = new byte[0]; + DataBuffer buffer = DefaultDataBufferFactory.sharedInstance.wrap(bytes); + + httpHeaders.setContentType(MediaType.APPLICATION_JSON); + given(mockResponse.getStatusCode()).willReturn(HttpStatus.NOT_FOUND); + given(mockResponse.getBody()).willReturn(Flux.just(buffer)); + + given(mockExchangeStrategies.messageReaders()).willReturn(List.of( + new DecoderHttpMessageReader<>(new ByteArrayDecoder()), + new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()))); + + WebClientResponseException ex = defaultClientResponse.createException().block(); + assertThat(ex.getResponseBodyAs(Map.class)).isNull(); + } + @Test @SuppressWarnings("deprecation") void createError() {