Spring 5
WebClient
ClientResponse
response header
status code

How to extract response header status code from Spring 5 WebClient ClientResponse

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

With Spring 5 WebClient, you can extract the HTTP status code and headers either by working with ClientResponse directly or by letting Spring wrap the result in a ResponseEntity. The right choice depends on whether you need low-level control before reading the body.

If you only need body plus metadata, toEntity(...) is often the simplest route. If you need to inspect ClientResponse first and decide what to do based on status or headers, use exchangeToMono.

Use exchangeToMono When You Need ClientResponse

exchangeToMono gives you direct access to the ClientResponse, including status code and headers.

java
1import org.springframework.http.HttpStatusCode;
2import org.springframework.web.reactive.function.client.WebClient;
3import reactor.core.publisher.Mono;
4
5WebClient client = WebClient.builder()
6    .baseUrl("https://api.example.com")
7    .build();
8
9Mono<String> result = client.get()
10    .uri("/users/1")
11    .exchangeToMono(response -> {
12        HttpStatusCode status = response.statusCode();
13        String requestId = response.headers().asHttpHeaders().getFirst("X-Request-Id");
14
15        System.out.println("Status: " + status.value());
16        System.out.println("Request ID: " + requestId);
17
18        if (status.is2xxSuccessful()) {
19            return response.bodyToMono(String.class);
20        }
21
22        return response.createError();
23    });

This is the most flexible option because you can branch on the status before deciding how to read the body.

Access Specific Headers

Headers are available through response.headers().asHttpHeaders().

java
1client.get()
2    .uri("/users")
3    .exchangeToMono(response -> {
4        int statusCode = response.statusCode().value();
5        String rateLimit = response.headers().asHttpHeaders().getFirst("X-RateLimit-Remaining");
6        long contentLength = response.headers().contentLength().orElse(-1L);
7
8        System.out.println("Status: " + statusCode);
9        System.out.println("Rate limit remaining: " + rateLimit);
10        System.out.println("Content length: " + contentLength);
11
12        return response.bodyToMono(String.class);
13    });

The important point is to read the metadata while you still have the ClientResponse in hand.

Use toEntity(...) for the Easiest Body Plus Metadata Result

If you do not need custom branching on the raw response, retrieve().toEntity(...) is usually simpler.

java
1import org.springframework.http.ResponseEntity;
2import reactor.core.publisher.Mono;
3
4Mono<ResponseEntity<String>> entityMono = client.get()
5    .uri("/users/1")
6    .retrieve()
7    .toEntity(String.class);
8
9entityMono.subscribe(entity -> {
10    System.out.println("Status: " + entity.getStatusCode().value());
11    System.out.println("ETag: " + entity.getHeaders().getETag());
12    System.out.println("Body: " + entity.getBody());
13});

This is often the cleanest answer when your real goal is simply "give me status, headers, and body together."

retrieve() Versus exchangeToMono

The difference is mostly about control.

  • 'retrieve() is higher-level and convenient.'
  • 'exchangeToMono exposes the raw ClientResponse.'

If you use retrieve(), 4xx and 5xx responses are usually turned into errors automatically unless you customize that behavior. With exchangeToMono, you are responsible for inspecting the status and deciding what to do.

Blocking Example for Non-Reactive Code

If you are in a non-reactive context, you can still block for the result.

java
1ResponseEntity<String> entity = client.get()
2    .uri("/users/1")
3    .retrieve()
4    .toEntity(String.class)
5    .block();
6
7int status = entity.getStatusCode().value();
8String body = entity.getBody();
9String contentType = entity.getHeaders().getFirst("Content-Type");

This is fine in tests or imperative application code. It is not appropriate inside an already reactive WebFlux request pipeline.

Pick the API That Matches the Return Shape

If your method should return only a deserialized body, retrieve() keeps the code compact. If your method must preserve transport metadata such as status, caching headers, or rate-limit headers, make that explicit in your design by returning ClientResponse-derived data or a ResponseEntity.

Common Pitfalls

  • Using retrieve() when you really needed low-level access to ClientResponse before body extraction.
  • Forgetting that retrieve() treats error status codes differently from exchangeToMono.
  • Calling .block() inside reactive request handling and then wondering why the pipeline stalls or fails.
  • Extracting only the body and then realizing later that you also needed headers or status metadata.
  • Ignoring the response body entirely when working with ClientResponse, which can lead to poor resource handling patterns.

Summary

  • Use exchangeToMono when you need direct access to ClientResponse, including status and headers.
  • Use retrieve().toEntity(...) when you want the simplest way to get body plus response metadata together.
  • Read headers through response.headers().asHttpHeaders() or ResponseEntity.getHeaders().
  • Be explicit about how you want to handle non-2xx responses.
  • Avoid .block() inside reactive request pipelines.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.