Spring WebFlux — WebClient

In this article, will see how we could use Spring WebFlux WebClient for making non-blocking HTTP requests.

WebClient — Non-blocking, reactive client to perform HTTP requests, exposing a fluent, reactive API over underlying HTTP client libraries such as Reactor Netty.
Use static factory methods create() or create(String), or builder() to prepare an instance.
For examples with a response body see:
- retrieve()
- exchangeToMono()
- exchangeToFlux()

For examples with a request body see:
- bodyValue(Object)
- body(Publisher,Class)

db.json

{
"posts":[
{
"id":1,
"title":"json-server",
"author":"typicode"
}
],
"comments":[
{
"id":1,
"body":"some comment",
"postId":1
}
]
}

Start the command using Docker

docker run -d -p 3000:80 -v ${PWD}/db.json:/data/db.json clue/json-server

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.vinsguru</groupId>
<artifactId>r2dbc-transaction</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>r2dbc-transaction</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>

Post.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Post {
private int id;
private String title;
private String author;
}

Comment.java

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Comment {
private int id;
private String body;
private int postId;
}

Test class

import com.vinsguru.r2dbctransaction.entity.Post;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;

import java.time.Duration;
import java.util.Arrays;

@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class WebClientTest {
private static final String BASE_URL = "http://localhost:3000/";

private WebClient webClient;

@BeforeAll
public void beforeAll(){
webClient = WebClient.builder()
.baseUrl(BASE_URL).build();

}

@Test
public void testGET(){
this.webClient
.get()
.uri("/posts")
.retrieve()
.bodyToMono(Post[].class)
.flatMapIterable(Arrays::asList)

.subscribe(System.out::println);
}

@Test
public void savePosts(){
Post post = new Post();
post.setId(3);
post.setAuthor("Aravind");
post.setTitle("Life of NTC");

this.webClient
.post()
.uri("/posts")
.bodyValue(post)
.retrieve()
.bodyToMono(String.class)
.subscribe(System.out::println);
}

@Test
public void updatePost(){
Post post = new Post();
post.setId(3);
post.setAuthor("Aravind");
post.setTitle("Life of NTC 11");

webClient.put()
.uri("/posts/3")
.bodyValue(post)
.retrieve()
.bodyToMono(String.class)
.subscribe(System.out::println);
}

@Test
public void patchPosts(){
this.webClient
.patch()
.uri("/posts/3")
.bodyValue(BodyInserters.fromFormData("title", "Life of NTC 20"))
.retrieve()
.bodyToMono(String.class)
.subscribe(System.out::println);
}

@Test
public void deletePost(){
this.webClient
.delete()
.uri("/posts/3")
.retrieve()
.bodyToMono(String.class)
.subscribe(System.out::println);
}

@Test
public void timeoutPost(){
this.webClient
.get()
.uri("/posts")
.retrieve()
.bodyToMono(Post[].class)
.timeout(Duration.ofSeconds(1))
.flatMapIterable(Arrays::asList)
.onErrorReturn(new Post(100, "Default", "Default"))

.subscribe(System.out::println);
}
}

All endpoints-

// all posts 
http://localhost:3000/posts

// all comments
http://localhost:3000/comments

// post 1 comments
http://localhost:3000/posts/1/comments

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
PA

PA

Java Developer and enthusiast