Home  >  Spring 5

Spring WebFlux POST Request

By Arvind Rai, September 14, 2020
This page will walk through Spring WebFlux POST request example using functional programming. We can create REST reactive API for POST request using @PostMapping and @Controller annotations as well as using functional programming with RouterFunctions. To write the client code, Spring provides WebClient API.
Here we will create a Spring Boot WebFlux application to serve POST requests using functional programming. 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 POST Request Router

To create POST request endpoint using functional programming, Spring provides RouterFunctions class. To build the routers, RouterFunctions has RouterFunctions.Builder as inner class that has the methods such as GET(), POST(), PUT(), DELETE() etc. Here we will discuss POST() method.
1. The following POST() method adds a route to the given handler function matching the given URL pattern.
RouterFunctions.Builder POST(String pattern,
                             HandlerFunction<ServerResponse> handlerFunction) 
For the matching URL pattern of POST requests, the specified handlerFunction will execute.
2. The following POST() method adds a route to the given handler function matching the given URL pattern and predicate.
RouterFunctions.Builder POST(String pattern,
                             RequestPredicate predicate,
                             HandlerFunction<ServerResponse> handlerFunction) 

Find the sample router code that uses both variation of POST() method.
@Bean
public RouterFunction<ServerResponse> root(BookHandler bookHandler) {
	return RouterFunctions.route()
			  .POST("/createBook", bookHandler::createBook)
			  .POST("/saveWriter", RequestPredicates.contentType(MediaType.APPLICATION_JSON), bookHandler::saveWriter)
			  .build();
} 

Creating Handler

To create handler function for HTTP requests, create a method that returns Mono<ServerResponse> and accepts ServerRequest as method argument.
public Mono<ServerResponse> createBook(ServerRequest request) {
	return request.bodyToMono(Book.class)
			.flatMap(book -> Mono.just(bookService.createBook(book)))
			.flatMap(book -> ServerResponse.created(URI.create("/books/" + book.getId()))
					.contentType(MediaType.APPLICATION_JSON)
					.body(BodyInserters.fromValue(book)));
} 
The Mono is a reactive stream publisher API that completes successfully by emitting an element or with an error. The ServerRequest and ServerResponse are Spring APIs that represents server-side HTTP request and response respectively.

Creating Client to POST Data

To create client to interact with Spring REST reactive APIs, Spring provides WebClient API. The WebClient is a Spring non-blocking reactive client that performs HTTP requests, reactive API over underlying HTTP client libraries such as Reactor Netty. Find the code to send POST request.
WebClient client = WebClient.create("http://localhost:8080");
client.post()
	.uri("/create")
	.bodyValue(new Book("Android"))
	.exchange()
	.flatMap(res -> res.bodyToMono(Book.class))
	.subscribe(book -> System.out.println("POST: " + book.getId() + ", " + book.getName())); 

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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
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}", RequestPredicates.accept(MediaType.TEXT_PLAIN), bookHandler::getBookById)
		  .POST("/create", RequestPredicates.contentType(MediaType.APPLICATION_JSON), bookHandler::createBook)
		  .build();
	}
} 
BookHandler.java
package com.concretepage;
import java.net.URI;
import org.springframework.beans.factory.annotation.Autowired;
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 {
	@Autowired
	private BookService bookService;
	
	public Mono<ServerResponse> getBookById(ServerRequest request) {
		int id = Integer.parseInt(request.pathVariable("id"));
		return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
			.body(BodyInserters.fromValue(bookService.getBookById(id)));
	}
	public Mono<ServerResponse> createBook(ServerRequest request) {
		return request.bodyToMono(Book.class)
				.flatMap(book -> Mono.just(bookService.createBook(book)))
				.flatMap(book -> ServerResponse.created(URI.create("/books/" + book.getId()))
						.contentType(MediaType.APPLICATION_JSON)
						.body(BodyInserters.fromValue(book)));
	}
} 
BookService.java
package com.concretepage;
import org.springframework.stereotype.Service;
@Service
public class BookService {
	public Book getBookById(int id) {
                return new Book(id, "Spring Tutorials");
	}
	public Book createBook(Book book) {
		return new Book(102, book.getName());
	}
} 
Book.java
package com.concretepage;
public class Book {
	private int id;
	private String name;
	public Book() {}
	public Book(String name) {
		this.name = name;
	}
	public Book(int id, String name) {
		this.id = id;
		this.name = name;
	}
        //Sets and Gets
} 
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);
		BookWebClient bwc = new BookWebClient();
		bwc.fetchBookByIdDemo();
		bwc.createBookDemo();
	}
} 

Client Code using WebClient

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

	public void fetchBookByIdDemo() {
		  int id = 101;
		  client.get()
			.uri("/books/" + id)
			.exchange()	
		        .flatMap(res -> res.bodyToMono(String.class))
		        .subscribe(book -> System.out.println("GET: " + book));
	}
	public void createBookDemo() {
		  client.post()
			.uri("/create")
			.bodyValue(new Book("Android"))
			.exchange()
		        .flatMap(res -> res.bodyToMono(Book.class))
			.subscribe(book -> System.out.println("POST: " + book.getId() + ", " + book.getName())); 
	}	
} 
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 
Find the print screen of the output.
Spring WebFlux POST Request

References

Spring RouterFunctions
Spring WebFlux

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI








©2020 concretepage.com | Privacy Policy | Contact Us