springspring-webfluxwebflux

WebClient exchangeToMono retrieving empty body


When using WebClient's exchangeToMono() the body retrieving part is always returning an empty Mono:

Example, the exposed service which returns a non-empty Mono

@PostMapping("/test")
public Mono<Pojo> getCalled(@RequestBody Pojo pojo) {
    System.out.println(pojo); // always prints a non-null object
    return Mono.just(pojo);
}

WebClient with .retrieve()

WebClient.create().post().uri(theUrl).bodyValue(p).retrieve().toEntity(Pojo.class).map(response -> {
    if (response.getStatusCode().isError()) {
        // do something;
    }
    return response.getBody();
}).single(); // always get a single element and does not fail

WebClient with .exchangeToMono()

WebClient.create().post().uri(theUrl).bodyValue(p).exchangeToMono(Mono::just).flatMap(response -> {
    if (response.statusCode().isError()) {
        // do something;
    }

    return response.bodyToMono(Pojo.class).single(); // fails with java.util.NoSuchElementException: Source was empty
});

Am I doing something wrong?


Solution

  • i have no idea why you are doing exchangeToMono(Mono::just).flatMap(response -> ...) as it makes no sense.

    No you don't need to consume the Mono twice, what you need is to read the documentation on how to use the exchange function properly WebClient > Exchange

    Here is the example taken from the docs

    Mono<Person> entityMono = client.get()
            .uri("/persons/1")
            .accept(MediaType.APPLICATION_JSON)
            .exchangeToMono(response -> {
                if (response.statusCode().equals(HttpStatus.OK)) {
                    return response.bodyToMono(Person.class);
                }
                else {
                    // Turn to error
                    return response.createException().flatMap(Mono::error);
                }
            });
    

    And here is your code, but doing the things in the exchangeToMono instead

    WebClient.create().post().uri(theUrl).bodyValue(p).exchangeToMono(response -> {
        if (response.statusCode().isError()) {
            // do something;
        }
    
        return response.bodyToMono(Pojo.class).single();
    });
    

    exchangeToMono has a purpose, its not there to just put stuff into a Mono<T> its there because you need to consume the response so that the server can release the connection that has been allocated. You do that by doing your checks etc in the exchangeToMono and then extract the body to a Mono<T>.