Spring WebFlux GET Request

By Arvind Rai, September 11, 2020
This page will walk through Spring WebFlux GET request example. We can create GET request endpoint using functional programming as well as annotation based programming. The annotation based GET request endpoint is same as we do in Spring MVC using @GetMapping and @Controller. For the functional endpoint, we need to use Spring RouterFunction API. Find the sample code to create GET request endpoint.
1. Using functional programming.
@Bean
public RouterFunction<ServerResponse> root(BookHandler bookHandler) {
	return RouterFunctions.route()
	  .GET("/books/{id}", accept(MediaType.TEXT_PLAIN), bookHandler::getBookById)
	  .build();
} 
2. Using annotation.
@GetMapping("/books/{id}")
public Mono<Book> getBookById(@PathVariable("id") Integer id) {
	return service.getBookById(id);
} 
On this page we will create a Spring Boot WebFlux application for GET request endpoint using functional programming. Spring WebFlux can return reactive libraries such as Reactor, RxJava etc. By default Spring Boot uses Reactor as reactive library and Netty as server.

Technologies Used

Find the technologies being used in our example.
1. Java 11
2. Spring 5.2.8.RELEASE
3. Spring Boot 2.3.2.RELEASE
4. Maven 3.5.2

Creating GET Request Functional Endpoint

To create a method to accept HTTP GET using functional web framework, we need to use RouterFunctions.Builder. The RouterFunctions is central class to handle reactive functional web framework. The RouterFunctions has inner class as RouterFunctions.Builder that works as a builder for router functions. The RouterFunctions.Builder has methods such as GET(), POST(), DELETE(), HEAD() etc. Find the GET() method signature.
1.
RouterFunctions.Builder GET(String pattern, HandlerFunction<ServerResponse> handlerFunction) 
The given handler function handles all the GET requests that matches the given URL pattern.
pattern: URL pattern to match.
handlerFunction: handler function to be executed when given URL pattern matches.

2.
RouterFunctions.Builder GET(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction) 
The given handler function handles all the GET requests that matches the given URL pattern and predicate.

Find the below code to create GET request.
@Bean
public RouterFunction<ServerResponse> root(BookHandler bookHandler) {
    return RouterFunctions.route()
      .GET("/books/{id}", bookHandler::getBookById)
      .GET("/books", accept(MediaType.TEXT_PLAIN), bookHandler::getAllBooks)
      .build();
} 

Creating HandlerFunction

The HandlerFunction is a functional interface that handles a request. Spring Boot uses reactor API by default. Finds these two reactor APIs.
reactor.core.publisher.Mono: A reactive stream publisher API that completes successfully by emitting an element or with an error.
reactor.core.publisher.Flux: A reactive stream publisher API that completes successfully by emitting 0 to N element or with an error.

Find the sample use of Mono.
@Component
public class BookHandler {
	public Mono<ServerResponse> getAllBooks(ServerRequest request) {
		return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
			.body(BodyInserters.fromValue(Book.getAllBooks()));
	}	
} 
ServerRequest: Spring API that represents server-side HTTP request.
ServerResponse: Spring API that represents server-side HTTP response.

Complete Example using Spring Boot

pom.xml
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.3.2.RELEASE</version>
	<relativePath/>
</parent>
<properties>
	<java.version>11</java.version>
</properties>
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-webflux</artifactId>
	</dependency>
</dependencies> 
BookRouter.java
package com.concretepage;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
@Configuration
public class BookRouter {
	@Bean
	public RouterFunction root(BookHandler bookHandler) {
		return RouterFunctions.route()
		  .GET("/books/{id}", accept(MediaType.TEXT_PLAIN), bookHandler::getBookById)
		  .GET("/books", accept(MediaType.TEXT_PLAIN), bookHandler::getAllBooks)
		  .build();
	}
} 
BookHandler.java
package com.concretepage;
import java.util.function.Predicate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class BookHandler {
	public Mono<ServerResponse> getBookById(ServerRequest request) {
		int id = Integer.parseInt(request.pathVariable("id"));
		Predicate<Book> predicate = b -> b.getId() == id;
		Book book = Book.getAllBooks().stream().filter(predicate).findFirst().get();
		return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
			.body(BodyInserters.fromValue(book));
	}
	public Mono<ServerResponse> getAllBooks(ServerRequest request) {
		return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
			.body(BodyInserters.fromValue(Book.getAllBooks()));
	}	
} 
Book.java
package com.concretepage;
import java.util.Arrays;
import java.util.List;
public class Book {
	private int id;
	private String name;
	public Book() {}
	public Book(int id, String name) {
		this.id = id;
		this.name = name;
	}
        //Sets and Gets
	public static List<Book> getAllBooks() {
		return Arrays.asList(
				new Book(101, "Spring Tutorials"),
				new Book(102, "Hibernate Tutorials"),
				new Book(103, "Angular Tutorials")
		);
	}
} 
Application.java
package com.concretepage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
} 

Client Code with WebClient

BookWebClient.java
package com.concretepage;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public class BookWebClient {
	private WebClient client = WebClient.create("http://localhost:8080");

	public String fetchBookById(int id) {
		Mono<ClientResponse> result = client.get()
				.uri("/books/" + id)
				.exchange();	
		return result.flatMap(res -> res.bodyToMono(String.class)).block();
	}
	public String fetchAllBooks() {
		Mono<ClientResponse> result = client.get()
				.uri("/books")
				.exchange();	
		return result.flatMap(res -> res.bodyToMono(String.class)).block();
	}	
	public static void main(String[] args) {
		BookWebClient bwc = new BookWebClient();
		System.out.println("---Book by Id---");
		System.out.println(bwc.fetchBookById(102));
		System.out.println("---All books---");
		System.out.println(bwc.fetchAllBooks());
	}
} 
To run the application, download the code and go to root directory of the project using command prompt and run the command.
mvn spring-boot:run 
To test the application, run BookWebClient.java file. We can also access the URL.
http://localhost:8080/books 
Find the print screen.
Spring WebFlux GET Request

References

Spring RouterFunctions
Reactor Mono

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us