The response passed to exchangeToMono has a bounded ownership window; choose a body decoder or error path before returning.
Spring WebClient exchangeToMono: decode the response inside its callback
Inspect status with the body in scope
A shipping service returns 200 with a label, 404 when a parcel is missing, and 503 during maintenance. exchangeToMono lets the caller choose a decoder by status. After the returned publisher completes, any unconsumed response body is released; code farther downstream cannot decode it later. Return a domain error for known 404 and a service error for 5xx. Status policy should be consistent across blocking and reactive clients.
Keep size and time bounded
A bodyToMono call can buffer a response into memory. Choose a maximum in-memory size and a connection/read timeout suited to the payload. A reactive chain does not make an unbounded body safe. For a stream, use Flux and preserve backpressure rather than collecting every item. The deadline budget still includes waiting for a pooled connection, remote work and decoding.
Exercise disposal and status
Return a 503 with a body, a malformed 200 body, and a response that never completes. Assert typed errors and that the connection pool remains usable after each case. The snippet shows a status branch; exact error mapping and codecs need a stub-server integration test.
Implementation sketch
return shippingClient.get().uri("/labels/{parcelId}", parcelId)
.exchangeToMono(response -> {
if (response.statusCode().value() == 200) {
return response.bodyToMono(ShippingLabel.class);
}
if (response.statusCode().value() == 404) {
return Mono.error(new ParcelMissing(parcelId));
}
return response.createError();
});Cost and verification
Decoding a whole body retains up to the configured buffer limit. Leaking connections or allowing unlimited responses reduces throughput well before CPU is saturated.
Common Mistakes
- Do not save ClientResponse for body decoding after exchangeToMono completes.
- Do not treat every non-200 response as a missing parcel.
- Do not collect an unbounded response stream into one List.
Read next
Spring HTTP service client: the interface is a contract, not a transport policy, Spring RestClient 503 response: count attempts before adding a retry, Spring HTTP client timeouts: bound the call inside the request deadline, Reactive HTTP service client: the proxy returns a publisher, not a completed call.
