Overview
In this article, I would like to show you how we could use Spring WebClient for making non-blocking HTTP requests for various CRUD operations.
Spring WebClient
Spring WebFlux is a non-blocking asynchronous reactive web framework. It includes WebClient (something like RestTemplate) which provides fluent API for making HTTP requests in an asynchronous and non-blocking way. It also supports streaming responses.
Let’s see how to make a following HTTP method calls using Spring WebClient.
- GET
- POST
- PUT
- DELETE
Sample Application
To learn Spring WebClient, Let’s have some server with REST endpoints. It is very simple if you have docker installed.
- First create a simple JSON like this.
{
"posts":[
{
"id":1,
"title":"json-server",
"author":"typicode"
}
],
"comments":[
{
"id":1,
"body":"some comment",
"postId":1
}
]
}
- Save the file as db.json
- Run this docker command by mapping the file path with the container. It runs a json-server using the above json file as backend DB.
docker run -d -p 3000:80 -v ${PWD}/db.json:/data/db.json clue/json-server
- now you should be able to access the endpoints to access the data from our db.json
// all posts
http://localhost:3000/posts
// all comments
http://localhost:3000/comments
// post 1 comments
http://localhost:3000/posts/1/comments
Project Setup
Create a Spring project with the dependencies as shown here.
DTO
Let’s create a simple model for Post. I use lombok for getters and setters.
Post:
@Data
public class Post {
private int id;
private String title;
private String author;
}
Comment:
@Data
public class Comment {
private int id;
private String body;
private int postId;
}
Spring WebClient – Builder
Lets initialize few variables.
private static final String BASE_URL = "http://localhost:3000/";
private WebClient webClient;
I also create the new instance of immutable WebClient as shown here.
webClient = WebClient.builder()
.baseUrl(BASE_URL)
.build();
The WebClient has many other methods to customize which you could explore.
GET
The below method makes a GET request to the /posts endpoint which returns a Post[].
// http://localhost:3000/posts
webClient
.get()
.uri("/posts")
.retrieve()
.bodyToMono(Post[].class)
.flatMapIterable(Arrays::asList)
.subscribe(System.out::println);
In our case, we have only one post in the db. So I get the below output.
Post(id=1, title=json-server, author=typicode)
POST
Below sample code shows how we can post a DTO using WebClient.
// http://localhost:3000/posts
// create a new post object
Post post2 = new Post();
post2.setId(2);
post2.setAuthor("vinsguru");
post2.setTitle("project reactor");
//post via webclient
webClient
.post()
.uri("/posts")
.bodyValue(post2)
.retrieve()
.bodyToMono(String.class)
.subscribe(System.out::println);
Now accessing the below endpoint gives below JSON response.
// http://localhost:3000/posts
[
{
"id": 1,
"title": "json-server",
"author": "typicode"
},
{
"id": 2,
"title": "project reactor",
"author": "vinsguru"
}
]
PUT
To update/replace an existing record, we can use PUT request as shown here.
// http://localhost:3000/posts/2
// create a new post object
Post post2 = new Post();
post2.setId(2);
post2.setAuthor("vinsguru");
post2.setTitle("webclient"); // change the book title
//put via webclient
webClient
.put()
.uri("/posts/2")
.bodyValue(post2)
.retrieve()
.bodyToMono(String.class)
.subscribe(System.out::println);
Now I get below output when I access the endpoint.
// http://localhost:3000/posts
[
{
"id": 1,
"title": "json-server",
"author": "typicode"
},
{
"id": 2,
"title": "webclient",
"author": "vinsguru"
}
]
PATCH
To update just few fields instead of replacing the whole record.
Here we update the title to webclient-patch
// http://localhost:3000/posts/2
//patch via webclient
webClient
.patch()
.uri("/posts/2")
.body(BodyInserters.fromFormData("title", "webclient-patch"))
.retrieve()
.bodyToMono(String.class)
.subscribe(System.out::println);
DELETE
To delete a specific record from the DB.
// http://localhost:3000/posts/2
webClient
.delete()
.uri("/posts/2")
.retrieve()
.bodyToMono(String.class)
.subscribe(System.out::println);
TimeOut
The reactive WebClient is more resilient. We can set the max wait time for a HTTP request to finish and default object to return when the request gets timed out.
webClient
.get()
.uri("/posts")
.retrieve()
.bodyToMono(Post[].class)
.timeout(Duration.ofSeconds(1)) // max wait time 1 sec
.flatMapIterable(Arrays::asList)
.onErrorReturn(new Post(100, "default title", "default author"))
.subscribe(System.out::println);
Summary
We were able to successfully demonstrate making HTTP requests using Spring WebClient. If you are using Feign, we can also use Feign with WebFlux. Check this out.
Learn more about Spring WebFlux.
Happy learning 🙂
References: