Friends of OpenJDK Today

Spring 6.1 – RestClient

August 30, 2023

Author(s)

  • Simon Verhoeven

    Simon Verhoeven is a Java software engineer that's interested in sharing experience, and always up for an interesting conversation.

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:

Topics:

Related Articles

View All

Author(s)

  • Simon Verhoeven

    Simon Verhoeven is a Java software engineer that's interested in sharing experience, and always up for an interesting conversation.

Comments (0)

Your email address will not be published. Required fields are marked *

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Save my name, email, and website in this browser for the next time I comment.

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard