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
<code><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency></code>Create a Global RestClient
RestClient instances can be created with static methods such as
create(),
create(String url),
create(RestTemplate),
builder(), and
builder(RestTemplate).
<code>RestClient restClient = RestClient.builder()
.baseUrl(properties.getUrl())
.defaultHeader(HttpHeaders.AUTHORIZATION, encodeBasic("pig", "pig"))
.build();</code>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.
<code>String data = restClient.get()
.uri("?name={name}&type={type}", "lengleng", "1")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.body(String.class);
logger.info(data);</code>To obtain a
ResponseEntitywith status and headers, call
toEntityinstead of
body.
<code>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());</code>Convert Response to Bean
Spring automatically registers a Jackson message converter, allowing the response body to be mapped to a POJO.
<code>ReqUserResponse customer = restClient.get()
.uri("/{name}", "lengleng")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.body(ReqUserResponse.class);
logger.info("res name: " + customer.personInfo().name());</code>Fetching a list works similarly.
<code>List<ReqUserResponse> customers = restClient.get()
.uri("?type={type}", "1")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.body(List.class);
logger.info("res size " + customers.size());</code>Publish Data (POST)
Use
post()to send a new resource. The example creates a customer and checks the response status and location header.
<code>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());
}</code>Delete Data
Calling
delete()issues an HTTP DELETE request. When successful, the body is empty.
<code>ResponseEntity<Void> response = restClient.delete()
.uri("/{id}", 2)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.toBodilessEntity();
logger.info("Deleted with status " + response.getStatusCode());</code>Handle Errors
Client (4xx) or server (5xx) errors throw a
RestClientException. You can register a default status handler or use
onStatusfor per‑request handling.
<code>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();</code>Alternatively, use
onStatuson a specific request.
<code>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());
}</code>Exchange Method
The
exchangemethod lets you decode the response differently based on status codes, bypassing status handlers.
<code>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;
}
});</code>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.
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.