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