Use exchangeToMono when success, absence and failure have different body contracts, and keep decoding inside the exchange callback.
Spring WebClient status-specific decoding: consume one branch and close the rest
The status chooses the body type
A partner returns a receipt quote on success, an empty body for not-found and a problem document for other failures. retrieve is concise for a uniform success body, but exchangeToMono lets the client choose decoding based on status. The response body must be consumed or released within that exchange lifecycle; returning ClientResponse for later decoding breaks ownership. Response ownership covers the connection consequences of this boundary.
Preserve distinct business outcomes
A 404 for an optional quote can become Mono.empty, while a 401 or 503 must stay a failure. Do not turn every non-2xx into empty; that masks revoked credentials and partner outages as missing business data. The example delegates other failures to createError so the error body is handled by the client API. Add a separate bounded decoder when the partner's problem response needs structured fields, and keep codec memory limits in force for that error body too.
Test the release path
Feed a success body, an empty 404 and a failing response with a body through a test server. Reuse the connection after each path, and cancel a request while it is decoding to expose leaks. A downstream retry should be limited to operations that are safe to replay; decoding an error does not make a POST idempotent. Uncertain commit explains why a transport failure after a write needs a separate business key.
Implementation contract
Mono<ReceiptQuote> quote(String receiptId) {
return partnerClient.get()
.uri("/quotes/{receiptId}", receiptId)
.exchangeToMono(response -> {
if (response.statusCode().is2xxSuccessful()) {
return response.bodyToMono(ReceiptQuote.class);
}
if (response.statusCode().value() == 404) {
return Mono.empty();
}
return response.createError();
});
}Cost and verification
Status inspection is constant work. Body decoding and error buffering dominate memory and latency; a missing-result branch saves object allocation, but retries or repeated misses can still produce high request volume.
Common Mistakes
- Do not return ClientResponse for decoding after exchangeToMono completes.
- Do not map authentication and service failures to the same empty result as a true 404.
- Do not retry a write merely because its response body could not be decoded.
Read next
Spring WebClient exchangeToMono: decode the response inside its callback, Spring WebFlux codec memory budget: distinguish aggregation from streaming, Spring HTTP timeout after POST: the write may already exist, Spring RestClient 503 response: count attempts before adding a retry, Spring Boot tracing across RestClient: construct the client from Boot's builder.
Related delivery contract
Spring WebClient request audit: log route identity without payload leakage.
Related delivery contract
Spring WebClient attributes versus Reactor Context: choose the right request scope.
