Overview:
In this tutorial, I would like to show you how we can use Spring WebClient with Feign to make HTTP calls in reactive manner.
Spring WebClient with Feign:
Spring WebClient is a non-blocking reactive client to make HTTP requests. Feign is a library which helps us to create declarative REST clients easily with annotations and it provides better abstraction when we need to call an external service in Microservices Architecture.
In this tutorial, Lets see we could integrate these two.
Sample Application:
As the aim of this tutorial is to learn how we could integrate Spring WebClient with Feign and make HTTP requests, we need a service which exposes REST API for us to play with.
I am going to use json-server which exposes dummy REST-APIs.
- This json file (db.json) will act like a DB.
{ "movies":[ { "id":1, "title":"Lord of the Rings - The Fellowship of the Ring", "year": 2001, "imdbRating": 8.8 }, { "id":2, "title":"Lord of the Rings - The Two Towers", "year": 2002, "imdbRating": 8.7 }, { "id":3, "title":"Lord of the Rings - The Return of the King", "year": 2003, "imdbRating": 8.9 } ] }
- In the same location, I also have a docker-compose file which creates a json-server container. I have done volume mapping for the json-server to read our db.json.
version: '3' services: server: image: clue/json-server ports: - 3000:80 volumes: - ${PWD}/db.json:/data/db.json
- Once the docker container is up and running, we should be able to access below end points.
http://localhost:3000/movies
-
- GET a specific movie with id.
http://localhost:3000/movies/1
-
- POST - Add a new movie into the DB
http://localhost:3000/movies
-
- PUT - Update an existing movie by id
http://localhost:3000/movies/1
http://localhost:3000/movies/1
We have our movie-service ready. Lets create a Reactive Feign client to interact with this service.
Project Setup:
Lest create a Spring project with the following dependencies.
We also need to include this dependency.
<dependency> <groupId>com.playtika.reactivefeign</groupId> <artifactId>feign-reactor-spring-cloud-starter</artifactId> <version>3.1.0</version> <type>pom</type> </dependency>
DTO:
Lets create a DTO to represent the movie object.
@Data @ToString @NoArgsConstructor @AllArgsConstructor(staticName = "create") public class MovieDto { private Integer id; private String title; private Integer year; private Double imdbRating; }
Spring WebClient with Feign - MovieClient Interface:
Lets perform simple CRUD operations with our MovieService. For that lets create an interface as shown below.
@ReactiveFeignClient(value = "movie-service", url = "${movie.service.url}") public interface MovieClient { @RequestMapping(method = RequestMethod.GET, value = "movies") Flux<MovieDto> getAllMovies(); @RequestMapping(method = RequestMethod.GET, value = "movies/{movieId}") Mono<MovieDto> getMovie(@PathVariable("movieId") Integer movieId); @RequestMapping(method = RequestMethod.POST, value = "movies/") Mono<MovieDto> saveMovie(MovieDto movieDto); @RequestMapping(method = RequestMethod.PUT, value = "movies/{movieId}") Mono<Void> updateMovie(@PathVariable("movieId") Integer movieId, MovieDto movieDto); @RequestMapping(method = RequestMethod.DELETE, value = "movies/{movieId}") Mono<Void> deleteMovie(@PathVariable("movieId") Integer movieId); }
- Spring auto-configuration scans for @ReactiveFeignClient annotation and creates the beans automatically for us.
- movie.service.url can be injected via property file. In this case, I keep the value as shown below in the property file.
movie.service.url=http://localhost:3000
- We have some abstract methods for performing various CRUD operations. This is enough and Feign will take care of the REST (!).
- Lets NOT forget the @EnableReactiveFeignClients
@SpringBootApplication @EnableReactiveFeignClients public class WebClientFeignApplication { public static void main(String[] args) { SpringApplication.run(WebClientFeignApplication.class, args); } }
Service:
- Create a Service which implements CommandLineRunner.
- We can autowire MovieClient directly as shown below.
@Service public class FeignClientDemo implements CommandLineRunner { @Autowired private MovieClient movieClient; @Override public void run(String... args) throws Exception { } }
- Lets add methods one by one in the service class.
- GET all movies
private Mono<Void> getAll(){ return this.movieClient.getAllMovies() .doOnNext(System.out::println) .doFinally(s -> System.out.println("------------- GET All completed ------------------")) .then(); }
- POST - add new movie
- Here I keep the year as 1999. Later we will update it as 2001.
private Mono<Void> post(){ MovieDto dto = MovieDto.create( 5, "Harry Potter and the Sorcerer Stone", 1999, 7.6 ); return this.movieClient.saveMovie(dto) .doFinally(s -> System.out.println("------------- POST Movie completed ------------------")) .then(); }
- PUT - update a movie
- Here I correct the movie year from 1999 to 2001
private Mono<Void> put(){ MovieDto dto = MovieDto.create( null, "Harry Potter and the Sorcerer Stone", 2001, 7.6 ); return this.movieClient.updateMovie(5, dto) .doOnNext(System.out::println) .doFinally(s -> System.out.println("------------- Movie updated ------------------")) .then(); }
- GET a specific movie by id.
private Mono<Void> get(){ return this.movieClient.getMovie(5) .doOnNext(System.out::println) .doFinally(s -> System.out.println("------------- GET Movie completed ------------------")) .then(); }
private Mono<Void> delete(){ return this.movieClient.deleteMovie(5) .doOnNext(System.out::println) .doFinally(s -> System.out.println("------------- Movie deleted ------------------")) .then(); }
- Finally the run method invokes all these methods in the order as shown below.
@Override public void run(String... args) throws Exception { Flux.concat( getAll(), // first get all movies post(), // create a new movie id 5 get(), // get movie id 5 - check if it is present put(), // update movie id 5 get(), // get movie id 5 - check if it is updated delete() // delete movie id 5 ).subscribe(); }
Spring WebClient with Feign - Demo:
When I start the application, the run method invokes all the methods and we are able to interact with the MovieService. We get the response as shown below.
MovieDto(id=1, title=Lord of the Rings - The Fellowship of the Ring, year=2001, imdbRating=8.8) MovieDto(id=2, title=Lord of the Rings - The Two Towers, year=2002, imdbRating=8.7) MovieDto(id=3, title=Lord of the Rings - The Return of the King, year=2003, imdbRating=8.9) ------------- GET All completed ------------------ ------------- POST Movie completed ------------------ MovieDto(id=5, title=Harry Potter and the Sorcerer Stone, year=1999, imdbRating=7.6) ------------- GET Movie completed ------------------ ------------- Movie updated ------------------ MovieDto(id=5, title=Harry Potter and the Sorcerer Stone, year=2001, imdbRating=7.6) ------------- GET Movie completed ------------------ ------------- Movie deleted ------------------
Summary:
We were able to successfully demonstrate Spring WebClient with Feign to create a declarative reactive client to make HTTP requests without much effort.
The source code is available here.
Learn more about Spring WebFlux.
Happy coding
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.