1

Before the deprecation of WebClient.exchange method I used to get ClientResponse body as Flux<DataBuffer> and manipulated it.

In Spring 5.3 the exchange() method is deprecated and I would like to change the implementation as recommended:

@deprecated since 5.3 due to the possibility to leak memory and/or connections; please, use {@link #exchangeToMono(Function)}, {@link #exchangeToFlux(Function)}; consider also using {@link #retrieve()} ...

Tried to do the same call in the lambda passed to exchangeToMono, but clientResponse.bodyToFlux(DataBuffer::class.java) always return an empty flux; other experiments (i.e. getting body as mono string) also could not help to get the body.

What is the standard way to get the ClientResponse body in Spring 5.3 ?

I am looking for a low-level body representation: something like "data buffer", "byte array" or "input stream"; to avoid any kind of parsing/deserialisation.

Before Spring 5.3:

webClient
    .method(GET)
    .uri("http://somewhere.com")
    .exchange()
    .flatMap { clientResponse ->
       val bodyRaw: Flux<DataBuffer> = clientResponse.bodyToFlux(DataBuffer::class.java) 
       // ^ body as expected
           
       // other operations
    }

After Spring 5.3

webClient
    .method(GET)
    .uri("http://somewhere.com")
    .exchangeToMono { clientResponse ->
       val bodyRaw: Flux<DataBuffer> = clientResponse.bodyToFlux(DataBuffer::class.java)
       // ^ always empty flux
           
       // other operations
    }
diziaq
  • 6,881
  • 16
  • 54
  • 96

2 Answers2

2

The new exchangeToMono and exchangeToFlux methods expect the body to be decoded inside the callback. Check this GitHub issue for details.

Looking at your example, probably you could use retrieve, that is safer alternative, with bodyToFlux

webClient
        .method(GET)
        .uri("http://somewhere.com")
        .retrieve()
        .bodyToFlux(DataBuffer.class)

or toEntityFlux if you need access to response details like headers and status

webClient
        .method(GET)
        .uri("http://somewhere.com")
        .retrieve()
        .toEntityFlux(DataBuffer.class)

Handling errors

Option 1. Use onErrorResume and handle WebClientResponseException

webClient
        .method(GET)
        .uri("http://somewhere.com")
        .retrieve()
        .bodyToFlux(DataBuffer.class)
        .onErrorResume(WebClientResponseException.class, ex -> {
            if (ex.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
                // ignore 404 and return empty
                return Mono.empty();
            }

            return Mono.error(ex);
        });

Option 2. Use onStatus convenience method to get access to response.

webClient
        .method(GET)
        .uri("http://somewhere.com")
        .retrieve()
        .onStatus(status -> status.equals(HttpStatus.NOT_FOUND), res -> {
            // ignore 404 and return empty
            return Mono.empty();
        })
        .bodyToFlux(DataBuffer.class)

Both methods could be used to deserialize error response Getting the response body in error case with Spring WebClient

Alex
  • 4,987
  • 1
  • 8
  • 26
  • The problem with Entity is - it only works for 200 response. In case of error you have no access to headers and body to recover and create proper pass-through. – dpedro Jun 02 '22 at 13:22
  • 1
    @dpedro there are multiple ways to get access to the response. Updated the answer with more details – Alex Jun 02 '22 at 14:37
0

You can use

.retrieve()
.toEntityFlux(DataBuffer.class)
.map(...)

Of course in this solution firstly a ResponseEntity is created by spring, and they you map it into another one, but I didn't find any better solution.