Master Spring 6.1 RestClient: Build, Retrieve, and Manage HTTP Calls

This guide explains how to use Spring 6.1's new RestClient for synchronous HTTP calls, covering setup, request building, response handling, data conversion, error processing, and advanced exchange methods with full code examples.

macrozheng
macrozheng
macrozheng
Master Spring 6.1 RestClient: Build, Retrieve, and Manage HTTP Calls

Introduction

Spring provides two HTTP clients: RestTemplate (synchronous) and WebClient (reactive). Spring 6.1 M1 adds RestClient, a new synchronous client that shares the same infrastructure as WebClient.

Project Setup

Dependency

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Create a Global RestClient

RestClient instances can be created with static methods such as create(), create(String url), create(RestTemplate), builder(), and builder(RestTemplate).

RestClient restClient = RestClient.builder()
  .baseUrl(properties.getUrl())
  .defaultHeader(HttpHeaders.AUTHORIZATION, encodeBasic("pig", "pig"))
  .build();

Retrieve Data

Use the fluent API to send a GET request, specify query parameters, accept JSON, and retrieve the response body as a string or a typed object.

String data = restClient.get()
  .uri("?name={name}&type={type}", "lengleng", "1")
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .body(String.class);
logger.info(data);

To obtain a ResponseEntity with status and headers, call toEntity instead of body.

ResponseEntity<String> response = restClient.get()
  .uri("?name={name}&type={type}", "lengleng", "1")
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .toEntity(String.class);
logger.info("Status " + response.getStatusCode());
logger.info("Headers " + response.getHeaders());

Convert Response to Bean

Spring automatically registers a Jackson message converter, allowing the response body to be mapped to a POJO.

ReqUserResponse customer = restClient.get()
  .uri("/{name}", "lengleng")
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .body(ReqUserResponse.class);
logger.info("res name: " + customer.personInfo().name());

Fetching a list works similarly.

List<ReqUserResponse> customers = restClient.get()
  .uri("?type={type}", "1")
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .body(List.class);
logger.info("res size " + customers.size());

Publish Data (POST)

Use post() to send a new resource. The example creates a customer and checks the response status and location header.

ReqUserResponse newCustomer = new ReqUserResponse("lengleng-plus", "1");
ResponseEntity<Void> response = restClient.post()
  .accept(MediaType.APPLICATION_JSON)
  .body(newCustomer)
  .retrieve()
  .toBodilessEntity();
if (response.getStatusCode().is2xxSuccessful()) {
  logger.info("Created " + response.getStatusCode());
  logger.info("New URL " + response.getHeaders().getLocation());
}

Delete Data

Calling delete() issues an HTTP DELETE request. When successful, the body is empty.

ResponseEntity<Void> response = restClient.delete()
  .uri("/{id}", 2)
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .toBodilessEntity();
logger.info("Deleted with status " + response.getStatusCode());

Handle Errors

Client (4xx) or server (5xx) errors throw a RestClientException. You can register a default status handler or use onStatus for per‑request handling.

RestClient restClient = RestClient.builder()
  .baseUrl(properties.getUrl())
  .defaultHeader(HttpHeaders.AUTHORIZATION, encodeBasic("pig", "pig"))
  .defaultStatusHandler(HttpStatusCode::is4xxClientError, (req, res) -> {
    logger.error("Client Error Status " + res.getStatusCode());
    logger.error("Client Error Body " + new String(res.getBody().readAllBytes()));
  })
  .build();

Alternatively, use onStatus on a specific request.

ResponseEntity<Void> response = restClient.delete()
  .uri("/{id}", 2)
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .onStatus(HttpStatusCode::is4xxClientError, (req, res) ->
    logger.error("Couldn't delete " + res.getStatusText()))
  .toBodilessEntity();
if (response.getStatusCode().is2xxSuccessful()) {
  logger.info("Deleted with status " + response.getStatusCode());
}

Exchange Method

The exchange method lets you decode the response differently based on status codes, bypassing status handlers.

SimpleResponse simpleResponse = restClient.get()
  .uri("/{id}", 4)
  .accept(MediaType.APPLICATION_JSON)
  .exchange((req, res) -> {
    switch (res.getStatusCode().value()) {
      case 200 -> SimpleResponse.FOUND;
      case 404 -> SimpleResponse.NOT_FOUND;
      default -> SimpleResponse.ERROR;
    }
  });

Conclusion

Compared with the older RestTemplate, the new RestClient API is easier to manage, aligns with the Loom‑based HTTP client standards, works well with JDK 21 virtual threads, and delivers higher performance for backend Java applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaspringHTTPSpringBootRestClient
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.