Skip to content

12.4.5 Exchanging requests

Up to this point, you’ve used the retrieve() method to signify sending a request when working with WebClient. In those cases, the retrieve() method returned an object of type ResponseSpec, through which you were able to handle the response with calls to methods such as onStatus(), bodyToFlux(), and bodyToMono(). Working with ResponseSpec is fine for simple cases, but it’s limited in a few ways. If you need access to the response’s headers or cookie values, for example, then ResponseSpec isn’t going to work for you.

When ResponseSpec comes up short, you can try calling exchangeToMono() or exchangeToFlux() instead of retrieve(). The exchangeToMono() method returns a Mono of type ClientResponse, on which you can apply reactive operations to inspect and use data from the entire response, including the payload, headers, and cookies. The exchangeToFlux() method works much the same way but returns a Flux of type ClientResponse for working with multiple data items in the response.

Before we look at what makes exchangeToMono() and exchangeToFlux() different from retrieve(), let’s start by looking at how similar they are. The following snippet of code uses a WebClient and exchangeToMono() to fetch a single ingredient by the ingredient’s ID:

java
Mono<Ingredient> ingredientMono = webClient
  .get()
  .uri("http://localhost:8080/ingredients/{id}", ingredientId)
  .exchangeToMono(cr -> cr.bodyToMono(Ingredient.class));

This is roughly equivalent to the next example that uses retrieve():

java
Mono<Ingredient> ingredientMono = webClient
  .get()
  .uri("http://localhost:8080/ingredients/{id}", ingredientId)
  .retrieve()
  .bodyToMono(Ingredient.class);

In the exchangeToMono() example, rather than use the ResponseSpec object’s bodyToMono() to get a Mono<Ingredient>, you get a Mono<ClientResponse> on which you can apply a flat-mapping function to map the ClientResponse to a Mono<Ingredient>, which is flattened into the resulting Mono.

Let’s see what makes exchangeToMono() different from retrieve(). Let’s suppose that the response from the request might include a header named X_UNAVAILABLE with a value of true to indicate that (for some reason) the ingredient in question is unavailable. And for the sake of discussion, suppose that if that header exists, you want the resulting Mono to be empty—to not return anything. You can achieve this scenario by adding another call to flatMap(), but now it’s simpler with a WebClient call like this:

java
Mono<Ingredient> ingredientMono = webClient
  .get()
  .uri("http://localhost:8080/ingredients/{id}", ingredientId)
  .exchangeToMono(cr -> {
    if (cr.headers().header("X_UNAVAILABLE").contains("true")) {
      return Mono.empty();
    }
    return Mono.just(cr);
  })
  .flatMap(cr -> cr.bodyToMono(Ingredient.class));

The new flatMap() call inspects the given ClientRequest object’s headers, looking for a header named X_UNAVAILABLE with a value of true. If found, it returns an empty Mono. Otherwise, it returns a new Mono that contains the ClientResponse. In either event, the Mono returned will be flattened into the Mono that the next flatMap() call will operate on.

Released under the MIT License.