Spring WebFlux POST Request
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.
Contents
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 providesRouterFunctions
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)
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 returnsMono<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))); }
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 providesWebClient
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>
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 RouterFunctionroot(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(); } }
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))); } }
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()); } }
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 }
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.javapackage 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())); } }
mvn spring-boot:run

References
Spring RouterFunctionsSpring WebFlux