Support Void response body type in WebClient
This commit adds support for Void response body types in the WebClient, both when using `exchange` with a response.bodyToMono(Void.class), as well as using `retrieve` with `toEntity(Void.class)`. Issue: SPR-15679
This commit is contained in:
@@ -19,6 +19,7 @@ package org.springframework.web.reactive.function;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
@@ -101,7 +102,8 @@ public abstract class BodyExtractors {
|
||||
return reader.readMono(elementType, inputMessage, context.hints());
|
||||
}
|
||||
},
|
||||
Mono::error);
|
||||
Mono::error,
|
||||
Mono::empty);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +151,8 @@ public abstract class BodyExtractors {
|
||||
return reader.read(elementType, inputMessage, context.hints());
|
||||
}
|
||||
},
|
||||
Flux::error);
|
||||
Flux::error,
|
||||
Flux::empty);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,8 +224,12 @@ public abstract class BodyExtractors {
|
||||
|
||||
private static <T, S extends Publisher<T>> S readWithMessageReaders(
|
||||
ReactiveHttpInputMessage inputMessage, BodyExtractor.Context context, ResolvableType elementType,
|
||||
Function<HttpMessageReader<T>, S> readerFunction, Function<Throwable, S> unsupportedError) {
|
||||
Function<HttpMessageReader<T>, S> readerFunction, Function<Throwable, S> unsupportedError,
|
||||
Supplier<S> empty) {
|
||||
|
||||
if (elementType.equals(ResolvableType.forClass(Void.class))) {
|
||||
return empty.get();
|
||||
}
|
||||
MediaType contentType = contentType(inputMessage);
|
||||
List<HttpMessageReader<?>> messageReaders = context.messageReaders();
|
||||
return messageReaders.stream()
|
||||
|
||||
@@ -387,11 +387,14 @@ class DefaultWebClient implements WebClient {
|
||||
|
||||
@Override
|
||||
public <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType) {
|
||||
return this.responseMono.flatMap(response ->
|
||||
response.bodyToMono(bodyType).map(body -> {
|
||||
return this.responseMono.flatMap(response -> {
|
||||
HttpHeaders headers = response.headers().asHttpHeaders();
|
||||
return new ResponseEntity<>(body, headers, response.statusCode());
|
||||
})
|
||||
HttpStatus statusCode = response.statusCode();
|
||||
return response.bodyToMono(bodyType)
|
||||
.map(body -> new ResponseEntity<>(body, headers, statusCode))
|
||||
.switchIfEmpty(Mono.defer(
|
||||
() -> Mono.just(new ResponseEntity<>(headers, statusCode))));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,12 +52,10 @@ public class WebClientIntegrationTests {
|
||||
|
||||
private WebClient webClient;
|
||||
|
||||
private String baseUrl;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.server = new MockWebServer();
|
||||
baseUrl = this.server.url("/").toString();
|
||||
String baseUrl = this.server.url("/").toString();
|
||||
this.webClient = WebClient.create(baseUrl);
|
||||
}
|
||||
|
||||
@@ -468,6 +466,35 @@ public class WebClientIntegrationTests {
|
||||
Assert.assertEquals(2, server.getRequestCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exchangeNoContent() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setHeader("Content-Length", "0"));
|
||||
|
||||
Mono<ClientResponse> result = this.webClient.get()
|
||||
.uri("/noContent")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result).assertNext(r -> {
|
||||
assertTrue(r.statusCode().is2xxSuccessful());
|
||||
StepVerifier.create(r.bodyToMono(Void.class)).verifyComplete();
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveNoContent() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setHeader("Content-Length", "0"));
|
||||
|
||||
Mono<ResponseEntity<Void>> result = this.webClient.get()
|
||||
.uri("/noContent")
|
||||
.retrieve()
|
||||
.toEntity(Void.class);
|
||||
|
||||
StepVerifier.create(result).assertNext(r -> {
|
||||
assertFalse(r.hasBody());
|
||||
assertTrue(r.getStatusCode().is2xxSuccessful());
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class MyException extends RuntimeException {
|
||||
|
||||
Reference in New Issue
Block a user