Skip to content

Commit 7a51617

Browse files
committed
WebClientResponseException supports decoding empty content
Closes gh-31179
1 parent 249f6f2 commit 7a51617

File tree

3 files changed

+46
-7
lines changed

3 files changed

+46
-7
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@
5050
import org.springframework.util.Assert;
5151
import org.springframework.util.MimeType;
5252
import org.springframework.util.MultiValueMap;
53+
import org.springframework.util.ObjectUtils;
5354
import org.springframework.web.reactive.function.BodyExtractor;
5455
import org.springframework.web.reactive.function.BodyExtractors;
5556

@@ -233,6 +234,9 @@ public Mono<WebClientResponseException> createException() {
233234

234235
private Function<ResolvableType, ?> initDecodeFunction(byte[] body, @Nullable MediaType contentType) {
235236
return targetType -> {
237+
if (ObjectUtils.isEmpty(body)) {
238+
return null;
239+
}
236240
Decoder<?> decoder = null;
237241
for (HttpMessageReader<?> reader : strategies().messageReaders()) {
238242
if (reader.canRead(targetType, contentType)) {

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -233,22 +233,21 @@ public String getResponseBodyAsString(Charset defaultCharset) {
233233
*/
234234
@Nullable
235235
public <E> E getResponseBodyAs(Class<E> targetType) {
236-
return getResponseBodyAs(ResolvableType.forClass(targetType));
236+
return decodeBody(ResolvableType.forClass(targetType));
237237
}
238238

239239
/**
240-
* Variant of {@link #getResponseBodyAs(Class)} with
241-
* {@link ParameterizedTypeReference}.
240+
* Variant of {@link #getResponseBodyAs(Class)} with {@link ParameterizedTypeReference}.
242241
* @since 6.0
243242
*/
244243
@Nullable
245244
public <E> E getResponseBodyAs(ParameterizedTypeReference<E> targetType) {
246-
return getResponseBodyAs(ResolvableType.forType(targetType.getType()));
245+
return decodeBody(ResolvableType.forType(targetType.getType()));
247246
}
248247

249248
@SuppressWarnings("unchecked")
250249
@Nullable
251-
private <E> E getResponseBodyAs(ResolvableType targetType) {
250+
private <E> E decodeBody(ResolvableType targetType) {
252251
Assert.state(this.bodyDecodeFunction != null, "Decoder function not set");
253252
return (E) this.bodyDecodeFunction.apply(targetType);
254253
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.nio.charset.StandardCharsets;
2222
import java.util.Collections;
2323
import java.util.List;
24+
import java.util.Map;
2425
import java.util.OptionalLong;
2526

2627
import org.junit.jupiter.api.BeforeEach;
@@ -45,6 +46,7 @@
4546
import org.springframework.http.client.reactive.ClientHttpResponse;
4647
import org.springframework.http.codec.DecoderHttpMessageReader;
4748
import org.springframework.http.codec.HttpMessageReader;
49+
import org.springframework.http.codec.json.Jackson2JsonDecoder;
4850
import org.springframework.util.LinkedMultiValueMap;
4951
import org.springframework.util.MultiValueMap;
5052

@@ -346,6 +348,40 @@ void createException() {
346348
assertThat(exception.getResponseBodyAsByteArray()).isEqualTo(bytes);
347349
}
348350

351+
@Test
352+
void createExceptionAndDecodeContent() {
353+
byte[] bytes = "{\"name\":\"Jason\"}".getBytes(StandardCharsets.UTF_8);
354+
DataBuffer buffer = DefaultDataBufferFactory.sharedInstance.wrap(bytes);
355+
356+
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
357+
given(mockResponse.getStatusCode()).willReturn(HttpStatus.NOT_FOUND);
358+
given(mockResponse.getBody()).willReturn(Flux.just(buffer));
359+
360+
given(mockExchangeStrategies.messageReaders()).willReturn(List.of(
361+
new DecoderHttpMessageReader<>(new ByteArrayDecoder()),
362+
new DecoderHttpMessageReader<>(new Jackson2JsonDecoder())));
363+
364+
WebClientResponseException ex = defaultClientResponse.createException().block();
365+
assertThat(ex.getResponseBodyAs(Map.class)).hasSize(1).containsEntry("name", "Jason");
366+
}
367+
368+
@Test
369+
void createExceptionAndDecodeWithoutContent() {
370+
byte[] bytes = new byte[0];
371+
DataBuffer buffer = DefaultDataBufferFactory.sharedInstance.wrap(bytes);
372+
373+
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
374+
given(mockResponse.getStatusCode()).willReturn(HttpStatus.NOT_FOUND);
375+
given(mockResponse.getBody()).willReturn(Flux.just(buffer));
376+
377+
given(mockExchangeStrategies.messageReaders()).willReturn(List.of(
378+
new DecoderHttpMessageReader<>(new ByteArrayDecoder()),
379+
new DecoderHttpMessageReader<>(new Jackson2JsonDecoder())));
380+
381+
WebClientResponseException ex = defaultClientResponse.createException().block();
382+
assertThat(ex.getResponseBodyAs(Map.class)).isNull();
383+
}
384+
349385
@Test
350386
@SuppressWarnings("deprecation")
351387
void createError() {

0 commit comments

Comments
 (0)