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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
