Spring 6.1 – RestClient

As you might have read in this blogpost, Spring is introducing a RestClient in Spring 6.1 to interact with HTTP backends.

Now some of you might be wondering as to the why, given we already have a plethora of other options such as RestTemplate, WebClient, HttpUrlConnection, …​

As we can see on the javadoc page RestTemplate got quite massive over time.

The Spring team drew lessons from this, and the reactive WebClient was created with a fluent interface.

Now, one can certainly use this one in place of RestTemplate, but that means dragging in extra dependencies, and well bodyToMono looks a bit “scary” the first time you see it, doesn’t it?

So the Spring team decided to introduce the RestClient which:

offers a fluent API
can be used with HTTP interfaces introduced in 6.0 (formerly only with WebClient)
allows us to achieve the same results as RestTemplate
uses a synchronous HTTP client

Now let’s have some fun with it, and please do feel free to check out the repository!

Usage

Using rest client builder

We start out by creating our rest client:

public JokeController(RestClient.Builder restClientBuilder, @Value(“${jokeserviceUrl}”) String jokeserviceUrl) {
this.restClient = restClientBuilder.baseUrl(jokeserviceUrl).build();
}

And then we can fetch a joke using:

return this.restClient
.get()
.uri(“/j/{jokeId}”, “R7UfaahVfFd”)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.body(Joke.class);

Which as you can see is well, a lot more fluid.

When doing this call we’ll be rewarded with:

{
“id”: “R7UfaahVfFd”,
“joke”: “My dog used to chase people on a bike a lot. It got so bad I had to take his bike away.”,
“status”: 200
}

Now in case we’re interested in the whole response including the status code, we can replace .body(Joke.class) with .toEntity(Joke.class).
Or in case of a call where the response does not interest us, we can use .toBodilessEntity().

Using a declarative HTTP interface

Now as mentioned at the start, one of the upsides of the new RestClient is that we can also use it for declarative HTTP clients.

Let’s declare an interface to fetch a joke:

interface JokeClient {
@GetExchange(url = “/j/{jokeId}”, accept = “application/json”)
Joke getJoke(@PathVariable String jokeId);
}

Then we can create our client using:

var factory = HttpServiceProxyFactory.builderFor(RestClientAdapter.create(restClient)).build();
jokeClient = factory.createClient(JokeClient.class);

After that, we can easily consume it using

@GetMapping(“/joke-using-interface”)
Joke getJokeUsingInterface() {
return this.jokeClient.getJoke(“M7wPC5wPKBd”);
}

And we’ll receive:

{
“id”: “M7wPC5wPKBd”,
“joke”: “Did you hear the one about the guy with the broken hearing aid? Neither did he.”,
“status”: 200
}

Error handling

By default, RestClient will throw a subclass of RestClientException upon a 4** or 5** status code, but we can override this using onStatus so that we can define our own status handlers:

.onStatus(HttpStatusCode::is4xxClientError, ((request, response) -> {
throw new PunException();
}))

More granular control

In some cases, we might want to do some more advanced things for which we need access to the underlying HTTP request or HTTP response. This can be achieved by usingexchange.

note: Status handlers are not applied when using exchange as you already have full access to the response, so you can perform any needed error handling.

return this.restClient
.get()
.uri(“/j/{jokeId}”, “UNKNOWN”)
.accept(MediaType.APPLICATION_JSON)
.exchange((clientRequest, clientResponse) -> {
// some criteria to determine our joke’s too punny
return “Singing in the shower is fun until you get soap in your mouth. Then it’s a soap opera”;
});

Wrap-up

I hope this sheds some light on the how, and why. And if you’re adding Webflux just to make use of WebClient please consider changing to RestClient.

In case you want to read a bit more about this:

RestClient introduction blogpost
RestTemplate javadoc

The post Spring 6.1 – RestClient appeared first on foojay.