Backend Development 12 min read

Mastering Spring 6 REST Calls: WebClient, RestTemplate, HTTP Interface & RestClient

Spring 6 offers four powerful ways to perform remote HTTP calls—WebClient, RestTemplate, HTTP Interface, and RestClient—each with distinct APIs, configuration options, and usage patterns, and this guide walks through their setup, method signatures, request handling, and error management for Java developers.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring 6 REST Calls: WebClient, RestTemplate, HTTP Interface & RestClient

Environment: Spring 6.1.1 + JDK 17.

1. Introduction

Spring 6 provides four main ways to call remote HTTP services: WebClient, RestTemplate, HTTP Interface, and RestClient.

WebClient

WebClient, introduced in Spring 5, is a reactive, non‑blocking HTTP client offering better performance and lower memory usage than RestTemplate.

RestTemplate

RestTemplate, introduced in Spring 3, offers a simple, synchronous API for remote calls and remains widely used, though it is now in maintenance mode.

HTTP Interface

The HTTP Interface lets you define a Java interface annotated with HTTP exchange metadata; Spring generates a proxy that handles the underlying HTTP communication.

RestClient

RestClient, added in Spring 6.1.1, is a synchronous client with a modern, fluent API that abstracts the underlying HTTP library.

2. Remote Call Details

RestTemplate usage

RestTemplate provides high‑level methods such as getForObject , getForEntity , postForObject , exchange , etc., to perform CRUD operations.

Example of initializing RestTemplate with Apache HttpComponents:

<code>RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());</code>

URI templates can be used with String variables or a Map<String,String> :

<code>String result = restTemplate.getForObject("http://pack.com/users/{userId}", String.class, 666);</code>

Headers can be set via exchange() with a RequestEntity :

<code>String uriTemplate = "http://pack.com/users/{userId}";
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42);
RequestEntity<Void> requestEntity = RequestEntity.get(uri)
    .header("x-api-token", "aabbcc")
    .build();
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
String responseHeader = response.getHeaders().getFirst("x-version");
String body = response.getBody();</code>

If a MappingJackson2HttpMessageConverter is on the classpath, responses can be directly mapped to objects:

<code>User user = restTemplate.getForObject("http://pack.com/users/{userId}", User.class, 666);</code>

RestTemplate registers a set of default message converters; you can customize them as needed.

WebClient example

<code>Mono<Person> result = client.get()
    .uri("/users/{id}", id).accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .bodyToMono(User.class);</code>

HTTP Interface example

<code>@HttpExchange(url = "/demos")
public interface DemoInterface {
    @PostExchange("/format3/{id}")
    Users queryUser(@PathVariable Long id);
}</code>
<code>@Service
public class DemoService {
    private final DemoInterface demoInterface;
    public DemoService() {
        WebClient client = WebClient.builder().baseUrl("http://localhost:8088/").build();
        HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
        this.demoInterface = factory.createClient(DemoInterface.class);
    }
    public Users queryUser(Long id) {
        return this.demoInterface.queryUser(id);
    }
}</code>

RestClient example

<code>// Default creation
RestClient restClient = RestClient.create();

// Custom builder
RestClient restClient = RestClient.builder()
    .requestFactory(new HttpComponentsClientHttpRequestFactory())
    .baseUrl("http://localhost:8088")
    .defaultUriVariables(Map.of("id", "888"))
    .defaultHeader("x-api-token", "aabbcc")
    .requestInterceptor((request, body, execution) -> {
        System.out.println("Interceptor");
        return execution.execute(request, body);
    })
    .requestInitializer(request -> {
        System.out.println("Initializer");
        request.getHeaders().add("x-version", "1.0.0");
    })
    .build();</code>
<code>Users users = customClient.get()
    .uri("/demos/users/{id}")
    .retrieve()
    .body(Users.class);</code>
<code>ResponseEntity<Void> response = restClient.post()
    .uri("/demos/users")
    .contentType(APPLICATION_JSON)
    .body(user)
    .retrieve()
    .toBodilessEntity();</code>

Error handling can be customized with onStatus callbacks.

<code>Users users = customClient.get()
    .uri("/demos/users/{id}")
    .retrieve()
    .onStatus(HttpStatusCode::isError, (req, resp) -> {
        throw new RuntimeException(resp.getStatusCode().toString() + " request error");
    })
    .body(Users.class);</code>

Conclusion

Spring 6 equips developers with a rich set of tools—WebClient, RestTemplate, HTTP Interface, and RestClient—to perform remote HTTP calls efficiently, offering high performance, low memory footprint, and flexible customization.

JavaSpringRESTRestTemplateWebClientRestClientHttp Interface
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.