Open In App

Spring Webflux WebClient

Last Updated : 01 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

A client for making HTTP requests is included in Spring WebFlux. The Reactor-based WebClient API allows a declarative mixture of asynchronous functionality without requiring knowledge of threads or concurrency. Depending on the same used to encode and decode request and response content on the server side, it is non-blocking and allows streaming.

Example of Spring Webflux WebClient:

WebClient client = WebClient.create("http://localhost:8080");
client
.get()
.uri("/posts")
.exchange()
.flatMapMany(res -> res.bodyToFlux(Post.class))
.log()
.subscribe(post -> System.out.println("post: " + post));

Implementation of Spring Webflux WebClient

Below are the steps to implement Spring Webflux WebClient.

Step 1: Add Maven Dependencies

Let’s update the pom.xml file with the ensuing dependencies:

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

Step 2: Create a WebClient Instance

There are three alternatives available. Making a WebClient object with default settings is the first step.

WebClient client = WebClient.create();

Starting a WebClient instance with a specified base URI is the second option:

WebClient client = WebClient.create("http://localhost:port_number");

Step 3: Build a client using the DefaultWebClientBuilder

Using the DefaultWebClientBuilder class to build a client allows for complete customisation, making it the third and most advanced option:

WebClient client = WebClient.builder()
.baseUrl("http://localhost:port_number")
.defaultCookie("cookieKey", "cookieValue")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultUriVariables(Collections.singletonMap("url", "http://localhost:port_number"))
.build();

Step 4: Create a WebClient Instance with Timeouts

The 30-second HTTP timeouts that are set by default are frequently too sluggish for our purposes. We can change this behaviour by creating an instance of HttpClient and configuring our WebClient to use it.

Java
// Create an instance of HttpClient with custom configurations
HttpClient httpClient = HttpClient.create()
  // Set the connection timeout to 3000 milliseconds
  .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
  // Set the response timeout to 3000 milliseconds
  .responseTimeout(Duration.ofMillis(3000))
  // Add handlers for read and write timeouts
  .doOnConnected(conn -> 
    conn.addHandlerLast(new ReadTimeoutHandler(3000, TimeUnit.MILLISECONDS))
      .addHandlerLast(new WriteTimeoutHandler(3000, TimeUnit.MILLISECONDS)));

// Create a WebClient with a ReactorClientHttpConnector using the configured HttpClient
WebClient client = WebClient.builder()
  .clientConnector(new ReactorClientHttpConnector(httpClient))
  .build();

Step 5: Getting Ready for a Request: Identify the Headers

The most widely used headers, such as “If-None-Match,” “If-Modified-Since,” “Accept,” and “Accept-Charset,” have extra support. Here is a perfect example of how to apply these values:

Java
// Create a ResponseSpec by configuring request headers and retrieval options
ResponseSpec responseSpec = headersSpec
  // Set the "Content-Type" header to "application/json"
  .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
  // Specify accepted media types as "application/json" and "application/xml"
  .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
  // Specify the accepted character set as UTF-8
  .acceptCharset(StandardCharsets.UTF_8)
  // Set the "If-None-Match" header to "*"
  .ifNoneMatch("*")
  // Set the "If-Modified-Since" header to the current date and time
  .ifModifiedSince(ZonedDateTime.now())
  // Perform the retrieval and obtain a ResponseSpec
  .retrieve();

Step 6: Bind to an Application Context

A basic code snippet might resemble this if we inject an instance of the ApplicationContext:

Java
// Autowire the ApplicationContext to access the Spring application context
@Autowired
private ApplicationContext context;

// Use WebTestClient to bind to the ApplicationContext for testing
WebTestClient testClient = WebTestClient.bindToApplicationContext(context)
  .build();

Step 7: Create a request

Once a WebTestClient object has been created, every subsequent action in the chain will resemble a WebClient until the exchange method—which is one way to obtain a response—provides the WebTestClient.

Java
// Use WebTestClient to perform integration testing on a server
WebTestClient
  // Bind the client to a specific server base URL
  .bindToServer()
    .baseUrl("http://localhost:8080")
    .build()
    // Perform a POST request to the specified URI ("/resource")
    .post()
    .uri("/resource")
  // Exchange the request and obtain the response
  .exchange()
    // Expect the HTTP status to be "Created" (201)
    .expectStatus().isCreated()
    // Expect the "Content-Type" header to be "application/json"
    .expectHeader().valueEquals("Content-Type", "application/json")
    // Expect the response body to have a JSON field with the value "value"
    .expectBody().jsonPath("field").isEqualTo("value");

Step 8: WebFlux Security

We would like to limit access to this endpoint to ADMIN role users only because it lets users switch out current employees. Thus, let’s expand our EmployeeController with a new method:

Java
// Define a Spring WebFlux controller endpoint for handling POST requests to "/update"
@PostMapping("/update")
// Method to update an employee and return a Mono<Employee> representing the updated employee
public Mono<Employee> updateEmployee(@RequestBody Employee employee) {
    // Delegate the update operation to the employeeRepository, which returns a Mono<Employee>
    return employeeRepository.updateEmployee(employee);
}




Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads