New in Spring 6.1: RestClient
Spring Framework 6.1 M2 introduces the
RestClient
, a new synchronous HTTP client.
As the name suggests,
RestClient
offers the fluent API of
WebClient
with the infrastructure of
RestTemplate
.
Fourteen years ago, when
RestTemplate
was introduced in Spring Framework 3.0, we quickly discovered that exposing every capability of HTTP in a template-like class resulted in too many overloaded methods.
In Spring Framework 5, we therefore used a fluent API for the reactive
WebClient
.
With
RestClient
we are introducing a HTTP client that offers an API similar to
WebClient
, and that uses the message converters, request factories, interceptors, and other underlying components of
RestTemplate
.
Creating a
RestClient
You can create a
RestClient
using one of the static
create
methods.
You can also use
RestClient::builder
to get a builder with further options, such as specifying the HTTP client to use, setting a default URL, path variables, and headers, or registering interceptors and initializers.
Using
RestClient::create(RestTemplate)
, you can initialize a
RestClient
with the configuration of an existing
RestTemplate
.
Retrieve
Let's create a
RestClient
, use it to set up a basic
GET
request, and retrieve the contents of a site as string using
retrieve
:
RestClient restClient = RestClient.create();
String result = restClient.get()
.uri("https://example.com")
.retrieve()
.body(String.class);
System.out.println(result);
If you're interested in the response status code and headers, and not just the contents, you can use toEntity to get a ResponseEntity:
ResponseEntity<String> result = restClient.get()
.uri("https://example.com")
.retrieve()
.toEntity(String.class);
System.out.println("Response status: " + result.getStatusCode());
System.out.println("Response headers: " + result.getHeaders());
System.out.println("Contents: " + result.getBody());
RestClient can also convert JSON to objects, using Jackson under the hood.
In fact, it can convert all types that RestTemplate supports, as it uses the same message converters.
Note the usage of uri variables, and that the Accept header is set to JSON.
int id = ...
Pet pet = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id)
.accept(APPLICATION_JSON)
.retrieve()
.body(Pet.class);
Doing a POST request is just as simple, like so:
Pet pet = ...
ResponseEntity<Void> response = restClient.post()
.uri("https://petclinic.example.com/pets/new")
.contentType(APPLICATION_JSON)
.body(pet)
.retrieve()
.toBodilessEntity();
Error handling
By default, RestClient throws a subclass of RestClientException when receiving a 4xx or 5xx status code.
This behavior can be overriden using status handlers, like so:
String result = restClient.get()
.uri("https://example.com/this-url-does-not-exist")
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders())
.body(String.class);
Exchange
The RestClient offers the exchange method for more advanced scenarios, as it provides access to the underlying HTTP request and response.
The previously mentioned status handlers are not applied when you use exchange, because the exchange function already provides access to the full response, allowing you to perform any error handling necessary:
Pet result = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id)
.accept(APPLICATION_JSON)
.exchange((request, response) -> {
if (response.getStatusCode().is4xxClientError()) {
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders());
else {
Pet pet = convertResponse(response);
return pet;