RestClient in Spring 6 with Examples

RestClient in Spring 6.1

RestClient in Spring 6 introduces a synchronous HTTP client with a modern, fluent API. This new client provides a convenient way to convert between Java objects and HTTP requests/responses, offering an abstraction over various HTTP libraries. In this guide, we’ll explore how to create and use RestClient with simple, easy-to-understand examples.

Adding Dependencies

To get started with RestClient, you need to add the spring-boot-starter-web dependency to your pom.xml file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Gradle

For a Gradle-based project, include the following dependency in your build.gradle file:

implementation 'org.springframework.boot:spring-boot-starter-web'

Configuring RestClient as a Spring Bean

To use RestClient effectively in your Spring application, it is recommended to define it as a Spring bean. This allows you to inject it into your services or controllers easily.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;

@Configuration
public class RestClientConfig {

    @Bean
    public RestClient restClient() {
        return RestClient.builder().build();
    }
}

Using the RestClient

To make an HTTP request with RestClient, start by specifying the HTTP method. This can be done using method(HttpMethod) or convenience methods like get(), post(), etc.

1. GET Request Example

First, let’s see how to perform a simple GET request.

Example: GET Request

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class ApiService {

    @Autowired
    private RestClient restClient;

    public String fetchData() {
        String response = restClient.get()
            .uri("https://api.example.com/data")
            .retrieve()
            .body(String.class);

        System.out.println(response);
        return response;
    }
}

In this example, we create a RestClient bean and inject it into our ApiService. We then use it to make a GET request to fetch data from https://api.example.com/data.

2. POST Request Example

Next, let’s see how to perform a POST request with a request body.

Example: POST Request

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

import java.util.Map;

@Service
public class OrderService {

    @Autowired
    private RestClient restClient;

    public ResponseEntity<Void> createOrder(Map<String, String> order) {
        return restClient.post()
            .uri("https://api.example.com/orders")
            .contentType(MediaType.APPLICATION_JSON)
            .body(order)
            .retrieve()
            .toBodilessEntity();
    }
}

In this example, we send a POST request to create a new order. The order data is passed as a Map<String, String> and converted to JSON automatically.

3. PUT Request Example

Let’s see how to perform a PUT request with a request body.

Example: PUT Request

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

import java.util.Map;

@Service
public class UpdateService {

    @Autowired
    private RestClient restClient;

    public ResponseEntity<Void> updateResource(int resourceId, Map<String, Object> updatedData) {
        return restClient.put()
            .uri("https://api.example.com/resources/{id}", resourceId)
            .contentType(MediaType.APPLICATION_JSON)
            .body(updatedData)
            .retrieve()
            .toBodilessEntity();
    }
}

In this example, we send a PUT request to update a resource identified by resourceId. The updated data is passed as a Map<String, Object> and converted to JSON automatically.

4. DELETE Request Example

Now, let’s see how to perform a DELETE request.

Example: DELETE Request

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class DeleteService {

    @Autowired
    private RestClient restClient;

    public ResponseEntity<Void> deleteResource(int resourceId) {
        return restClient.delete()
            .uri("https://api.example.com/resources/{id}", resourceId)
            .retrieve()
            .toBodilessEntity();
    }
}

In this example, we send a DELETE request to delete a resource identified by resourceId.

Handling Responses

You can access the HTTP response status code, headers, and body using ResponseEntity.

Example: Accessing ResponseEntity

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class UserService {

    @Autowired
    private RestClient restClient;

    public void getUserDetails() {
        ResponseEntity<String> responseEntity = restClient.get()
            .uri("https://api.example.com/users/1")
            .retrieve()
            .toEntity(String.class);

        System.out.println("Status code: " + responseEntity.getStatusCode());
        System.out.println("Headers: " + responseEntity.getHeaders());
        System.out.println("Body: " + responseEntity.getBody());
    }
}

RestClient in Spring 6: Error Handling

By default, RestClient throws a subclass of RestClientException for responses with 4xx or 5xx status codes. You can customize this behavior using onStatus.

Example: Custom Error Handling

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;

@Service
public class ErrorHandlingService {

    @Autowired
    private RestClient restClient;

    public String fetchDataWithErrorHandling() {
        try {
            return restClient.get()
                .uri("https://api.example.com/nonexistent")
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, response -> {
                    throw new CustomClientException("Client error: " + response.getStatusCode());
                })
                .body(String.class);
        } catch (RestClientException e) {
            e.printStackTrace();
            return "An error occurred";
        }
    }
}

Advanced Scenarios with Exchange

For advanced scenarios, RestClient provides access to the underlying HTTP request and response through the exchange() method. Status handlers are not applied when using exchange(), allowing for custom error handling.

Example: Advanced GET Request

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class AdvancedService {

    @Autowired
    private RestClient restClient;

    public Map<String, Object> getUser(int id) {
        return restClient.get()
            .uri("https://api.example.com/users/{id}", id)
            .accept(MediaType.APPLICATION_JSON)
            .exchange((request, response) -> {
                if (response.getStatusCode().is4xxClientError()) {
                    throw new CustomClientException("Client error: " + response.getStatusCode());
                } else {
                    ObjectMapper mapper = new ObjectMapper();
                    return mapper.readValue(response.getBody(), new TypeReference<Map<String, Object>>() {});
                }
            });
    }
}

Choosing Between RestTemplate vs RestClient vs WebClient

1. RestTemplate

  • Use Case:
    • Traditional Synchronous Applications: Use RestTemplate if you are working in a traditional Spring MVC application where synchronous HTTP calls suffice.
    • Simple CRUD Operations: For straightforward HTTP interactions such as fetching data from RESTful services using blocking calls.
  • Key Features:
    • Template-based API (getForObject, postForObject, etc.).
    • Synchronous blocking calls.
    • Well-established, widely used in existing Spring applications.
  • Example Scenario:
    • Integrating with legacy systems or existing codebases using synchronous HTTP communication.

2. RestClient

  • Use Case:
    • Modern Synchronous Applications: Choose RestClient for applications requiring more flexibility and control over HTTP requests and responses.
    • Enhanced Error Handling: When you need to handle specific HTTP status codes or exceptions with onStatus.
  • Key Features:
    • Fluent API (get, post, put, delete) with method chaining.
    • Built-in support for content negotiation and message converters.
    • Configurable request and response handling.
  • Example Scenario:
    • Building new applications in Spring Framework 6 that benefit from a modern, flexible synchronous HTTP client.
    • Customizing HTTP headers, request bodies, and error handling mechanisms.

3. WebClient

  • Use Case:
    • Reactive and Non-blocking Applications: Opt for WebClient in reactive applications leveraging Spring WebFlux.
    • High-Concurrency: When handling high volumes of requests concurrently with asynchronous processing.
  • Key Features:
    • Non-blocking and reactive API.
    • Functional style with operators like flatMap, map, etc., for composing requests and handling responses.
    • Supports both synchronous (blocking) and asynchronous (reactive) modes.
  • Example Scenario:
    • Developing microservices architectures or event-driven systems where responsiveness and scalability are critical.
    • Implementing real-time data streaming or processing pipelines using reactive programming principles.

Conclusion

RestClient in Spring Framework 6.1 offers a modern, fluent API for interacting with RESTful services. Its flexibility and ease of use make it a powerful tool for any Spring developer. Whether making simple GET requests or handling complex scenarios, RestClient provides the capabilities you need for efficient and effective HTTP communication.

By following this guide, you should now be well-equipped to use RestClient in your Spring applications, making your development process smoother and more efficient.

Related Articles

  1. What is Spring Boot and Its Features
  2. Spring Boot Starter
  3. Spring Boot Packaging
  4. Spring Boot Custom Banner
  5. 5 Ways to Run Spring Boot Application
  6. @ConfigurationProperties Example: 5 Proven Steps to Optimize
  7. Mastering Spring Boot Events: 5 Best Practices
  8. Spring Boot Profiles Mastery: 5 Proven Tips
  9. CommandLineRunners vs ApplicationRunners
  10. Spring Boot Actuator: 5 Performance Boost Tips
  11. Spring Boot API Gateway Tutorial
  12. Apache Kafka Tutorial
  13. Spring Boot MongoDB CRUD Application Example
  14. ChatGPT Integration with Spring Boot

Stay Updated!
Subscribe to our newsletter for more insightful articles on Spring Boot and Java development. Stay informed about the latest trends and best practices directly in your inbox.