StackTips

What is WebClient? How to use WebClient in Java SpringBoot?

nilan avtar
Nilanchala Panigrahy 馃尡

WebClient is a non-blocking, reactive HTTP client introduced in Spring 5.0, which is the reactive counterpart to the traditional RestTemplate in Spring Boot. It provides a simplified and intuitive API for making HTTP requests. It is designed to handle both synchronous and asynchronous operations.

To use WebClient in a Spring Boot application, follow these steps:

  • Add the WebClient dependency: Ensure you have the necessary dependencies in your project. If you're using Maven, add the following dependency to your pom.xml file:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
  • Create a WebClient instance: In your code, create an instance of WebClient using the WebClient.builder() method. You can customize the WebClient instance by setting properties such as timeouts, authentication, SSL configuration, and more.
WebClient webClient = WebClient.builder()
    .baseUrl("https://api.example.com")
    .build();
  • Make HTTP requests: Use the methods provided by the WebClient instance to make HTTP requests. WebClient offers methods like get(), post(), put(), delete(), etc., to perform different types of requests. You can chain these methods together to customize the request.
Mono<MyResponse> responseMono = webClient.get()
    .uri("/data/{id}", id)
    .retrieve()
    .bodyToMono(MyResponse.class);

WebClient Spring Boot Example

Let us now examine, how to use WebClient in real-time to consume the following APIs from the Spring Boot application.

GET
https://fakestoreapi.com/products - get all products
https://fakestoreapi.com/products/1 - get specific product based on id

POST
https://fakestoreapi.com/products - add a new product

DELETE
https://fakestoreapi.com/products/1 - get specific product based on id

Domain Model

Let us now declare a domain model for the Product object.

public class Product {

    private String image;
    private double price;
    private String description;
    private int id;
    private String title;
    private String category;

    //Getter, Setter
}

Consuming GET Endpoint using WebClient

Let us now create a service class ProductService that consumes the Fakestore API endpoints using WebClient.

@Service
public class ProductService {

    private final WebClient webClient;

    public ProductService() {
        // you may configure the base URL in applcation.properties 
        this.webClient = WebClient.create("https://fakestoreapi.com");
    }

    public Mono<Product[]> getProducts() {
        return webClient.get()
                .uri("/products")
                .retrieve()
                .bodyToMono(Product[].class);
    }

    public Mono<Product> getProductById(int productId) {
        return webClient.get()
                .uri("/products/{id}", productId)
                .retrieve()
                .bodyToMono(Product.class);
    }
}

The getProducts() method uses WebClient to make a GET request to the /products endpoint. The retrieve() method initiates the request and returns a ClientResponse object. The bodyToMono() method is then used to deserialize the response body into an array of Product objects. Finally, the result is returned as a Mono<Product[]>.

Now we can consume the ProductService and call getProducts() endpoint to return the list of products as follows:

Mono<Product[]> productsMono = productService.getProducts();
productsMono.subscribe(
        products -> {
            //TODO write your logic to handle list of products repose
        },
        error -> {
            // Handle error cases
            error.printStackTrace();
        }
);

Similarly. we can call getProductById() method to return a product specified by Id.

Mono<Product> productMono = productService.getProductById(1);
productMono.subscribe(
        product -> {
            //TODO write your logic to handle product response
        },
        error -> {
            // Handle error cases
            error.printStackTrace();
        }
);

Consuming POST Endpoint using WebClient

Now let us expand the ProductService class to add support for the addProduct() method. This method makes a POST request to the /products endpoint and sends the product information as the request body.

public Mono<Product> addProduct(Product product) {
    return webClient.post()
            .uri("/products")
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(product))
            .retrieve()
            .bodyToMono(Product.class);
}

The post() method is used to initiate the POST request, and the body() method with BodyInserters.fromValue() is used to set the request body with the provided product object. The contentType() method sets the request content type to application/json to indicate that the request body is in JSON format.

Now, to consume this new endpoint as follows:

Product product = new Product();
product.setTitle("test product");
product.setPrice(13.5);
product.setDescription("lorem ipsum set");
product.setImage("https://i.pravatar.cc");
product.setCategory("electronic");
Mono<Product> addedProductMono = productService.addProduct(product);
addedProductMono.subscribe(
        addedProduct -> {
            //TODO Added product 
        },
        error -> {
            // Handle error cases
            error.printStackTrace();
        }
);

Consuming DELETE Endpoint using WebClient

Now to delete a product, add the following deleteProduct() method to the ProductService.

public Mono<Void> deleteProduct(int productId) {
    return webClient.method(HttpMethod.DELETE)
            .uri("/products/{id}", productId)
            .retrieve()
            .bodyToMono(Void.class);
}

To consume this new endpoint and delete a product, you can use the ProductService as follows:

Mono<Void> deletedProductMono = productService.deleteProduct(productId);
deletedProductMono.subscribe(
        response -> {
            // TODO Product deleted successfully
        },
        error -> {
            // Handle error cases
            error.printStackTrace();
        }
);

How to implement basic authentication in WebClient?

To implement basic authentication with WebClient, we need to create a custom interceptor class that implements the ExchangeFilterFunction interface. The ExchangeFilterFunction interface represents a functional contract for intercepting and modifying the exchange between the WebClient and the remote server during an HTTP request/response cycle.

It allows applying custom logic such as modifying request headers, logging, adding authentication tokens, modifying the request body, handling retries, or performing custom transformations on the response.

Let us create a BasicAuthInterceptor class that implements the ExchangeFilterFunction interface.

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeFunction;
import reactor.core.publisher.Mono;

class BasicAuthInterceptor implements ExchangeFilterFunction {
    private final String username;
    private final String password;

    public BasicAuthenticationInterceptor(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
        HttpHeaders headers = request.headers();
        headers.setBasicAuth(username, password);
        headers.setContentType(MediaType.APPLICATION_JSON);
        return next.exchange(request);
    }
}

Here's an example of how you can configure WebClient to use the Interceptor for doing the basic authentication:

this.webClient = WebClient.builder()
        .baseUrl("https://fakestoreapi.com")
        .filter(new BasicAuthInterceptor("username", "password"))
        .build();

With this configuration, WebClient will automatically include the basic authentication credentials in the request headers for all requests made using that WebClient instance.

How to pass X-API-Key Header in WebClient request?

To pass an X-API-Key header in WebClient requests in Spring Boot, you can use the header() method provided by the WebClient's mutate() function. Here's an example of how you can include the X-API-Key header in WebClient requests:

WebClient webClient = WebClient.builder()
        .baseUrl("https://fakestoreapi.com")
        .build();

 return webClient.get()
        .uri("/products")
        .header(HttpHeaders.AUTHORIZATION, "X-API-Key: YOUR_API_KEY")
        .retrieve()
        .bodyToMono(Product[].class);

Sharing is caring!

Did you like what Nilanchala Panigrahy 馃尡 wrote? Thank them for their work by sharing it on social media.

nilan avtar

Nilanchala Panigrahy 馃尡

I'm a blogger, educator and a full stack developer. Mainly focused on Java, Spring and Micro-service architecture. I love to learn, code, make and break things.

Related articles