Spring Boot 3.2: The new RestClient API

Spring Boot 3.2 is available on the Spring Initializr and we got a brand new way to call http endpoints with it: The new RestClient.

You might ask yourself why the Spring team released another way to make http calls in addition to the three ways we already have in our toolbox. Let’s have a quick look at the existing APIs to see how the new RestClient fits into the picture.

The oldest way to make http calls is the RestTemplate from the Spring Web module introduced in Spring 3. With the RestTemplate you can make synchronous calls and it offers more than a dozen methods for different input and output scenarios. The configuration for it is kind of cryptic and the overloaded methods don’t make it any easier to use. The class is in maintenance mode since Spring 5 and will not be further worked on.

The modern way to make http calls in Spring is the WebClient. It has a fluent API for configuration of calls and offers asynchronous calls. However, you will need the Spring Reactive Web dependency on the classpath and not only the Spring Web one. The API uses reactive types and there is no way to make a synchronous call even if the server you are calling responds synchronously.

Since Spring Boot 6 we can simply declare interfaces for our http client, which will be implemented automatically by the framework. Under the hood the WebClient is used, but you can make synchronous calls too. You will still need the Spring Reactive Web dependency on the classpath. If you want to know more about declarative http client interfaces, check out my previous post about them.

The new RestClient offers a way to make synchronous calls like the RestTemplate but offers a fluent API for configuring http calls similar to the WebClient. You also only need the Spring Web dependency on the classpath. So the new RestClient gives you an easy way to call http endpoints without the sometimes unwanted Spring Reactive Web module. It is basically the client that the RestTemplate should have been from the start.

The RestClient API

Let’s have a look at the new RestClient API. You have two options to create a new RestClient: You can either use one of the static create methods or use a builder API.

Here is an example how to use the static create methods:

import org.springframework.web.client.RestClient;

public class RestClientExamples {
  private RestClient restClient1 = RestClient.create();
  private RestClient restClient2 = RestClient.create("https://jsonplaceholder.typicode.com");

}

In line (4) I create a new client with the no-args create factory method. There is also an overloaded version in line (5) that takes as String with the default root URL you want to call. There is a third version that takes a RestTemplate as a parameter to use the exact same configuration for your new RestClient. This comes in handy if you were already using a RestTemplate and want to upgrade to a RestClient.

Making a GET Request

The next example is a simple GET request against the json placeholder API. The JSON placeholder API exposes a dummy endpoint that you can use if you need some simple JSON data for testing purposes.

RestClient restClient = RestClient.create("https://jsonplaceholder.typicode.com");
Post[] posts = restClient.get()
  .uri("/posts")
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .body(Post[].class);

In line (2) I start the call using the get method of RestClient to start a get request. There are also corresponding methods for all other http methods (post, delete, put and patch). Next, we specify the URL we want to call by the uri method and set the accept header by the accept method. If you specified a base URL for the client in the create mathod , the uri method only takes the additional path the resource. If you used the no-args create method, you need to specify the full URL.

To execute the call I use the retrieve method in line (5). After that, in line (6) I specify that the body of the response should be marshalled into an array of Post objects by the body method. This will be done automatically in the background by the Jackson object mapper as we were using WebClient or RestTemplate.

You can also retrieve a singe Post by changing the URL to a specific ID and change the body type to a single Post.

Post post = restClient.get()
  .uri("/posts/1")
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .body(Post.class);

Setting Request Headers

If you need to set custom http headers for your request you can use the header method:

Post[] posts = restClient.get()
  .uri("/posts")
  .accept(MediaType.APPLICATION_JSON)
  .header("header1", "value")
  .header("header2", "value1", "value2", "value3")
  .retrieve()
  .body(Post[].class);

The header method takes one String parameter for the name of the header and after that a varargs of Strings for the values of the header.

Error Handling

If you want to react on certain http response codes, you can specify a custom handler for error handling by the onStatus method after calling retrieve:

Post post = restClient.get()
  .uri("/posts/1")
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .onStatus( status -> status.is5xxServerError(),
     (resp,requ) -> System.out.println("Error executing post request.") )
  .body(Post.class);

The onStatus method takes a Predicate<HttpStatus> that can perform a check on the http status code and an ErrorHandler which has access to the request and response to perform error handling. You can also specify multiple ErrorHandlers with subsequent onStatus calls.

Making a POST Request

If you want to make a post request, you also have the option to specify the request body in the body method after specifying the URL. This version of the body method takes the request body as a parameter.

Post newPost = new Post(5, null, "The Lord of the Rings", null);
Post post = restClient.post()
  .uri("/posts")
  .body(newPost)
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .body(Post.class);

Notice the body method in line (4) is not the same as the one in line (7). The first one takes the body for the outgoing request as an argument and is provided by the class RequestBodySpec. The second body call after the retrieve method takes the class of the incoming response type and is provided by the class ResponseSpec.

If you want to have access to the response headers and status code, you can also get the ResponseEntity of the call as a return type. Simply use the toEntity method of ResponseSpec instead of the body method. The toEntity method takes the class of the return type to unmarshall the body into the ResponseEntity.

ResponseEntity<Post> entity = restClient.get()
  .uri("/posts/1")
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .toEntity(Post.class);
  
HttpHeaders headers = entity.getHeaders();
Post body = entity.getBody();

Custom Exchange Methods

The last feature I want to show you is the use of a completely custom exchange method. This method is passed the request and response of the call and you have full control what to do with it and which return type it should be. This can also be used to log the request and response headers are change the response type dependent on response headers.

Here is a simple example where I log the response headers to the console:

Post post = restClient.get()
  .uri("/posts/1")
  .accept(MediaType.APPLICATION_JSON)
  .exchange((request, response) -> {
	System.out.println("Response Headers:" + response.getHeaders());
	ObjectMapper mapper = new ObjectMapper();
	String body = new String(response.getBody().readAllBytes());
	System.out.println("Response Body:" + body);
	return mapper.readValue(body, Post.class);
});

In line (4) I call the exchange method instead of retrieve. The method takes an ExchangeFunction as a parameter, which is a functional interface. The method takes the request and response as input parameters and return a type of your choice. In this example I manually unmarshall the response body to a Post object and log the response headers to the console.

I don’t want to go over the remaining http method, because they offer basically the same functionality as GET and POST.

Conclusion

The new RestClient is quite a neat addition to our toolbox to make http calls. The API is very structured, intuitive and gives you a lot of options to execute custom code in a clean and functional way. In my opinion this will be the main way to access http endpoints in the future with the reactive API getting less important. The introduction of virtual threads in Java 21 will give you a convenient way to asynchronously use the new RestClient without the need to write cryptic reactive code.

If you want to check out the code samples, you can find them here in github.


Beitrag veröffentlicht

in

von

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert