Spring 5 WebFlux - Reactive Web Application Functional Endpoint Getting Started Example

[Updated: Oct 30, 2017, Created: Oct 29, 2017]

Spring 5 WebFlux provides functional programming model for reactive web applications. Using this model is an alternative to using Spring MVC style annotations (last Example). This style routes a given HTTP requests via a RouterFunction (alternative to using annotations like @RequestMapping) and handles the request via HandlerFunction (alternative to @Controller's handler methods).

HandlerFunction

package org.springframework.web.reactive.function.server;
 .....
@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
    Mono<T> handle(ServerRequest request);
}

Where T is the type of the response of the function.

ServerRequest

This object represents a server-side HTTP request. We can access headers and body of the request via this object.

ServerResponse

This object represents a server-side HTTP response.

RouterFunction

package org.springframework.web.reactive.function.server;
 ....
@FunctionalInterface
public interface RouterFunction<T extends ServerResponse> {
    Mono<HandlerFunction<T>> route(ServerRequest request);
    ...
}

Creating a RouterFunction

To create a RouterFunction instance, we will typically use RouterFunctions utility class. For example following method will return the RouterFunction:

public static <T extends ServerResponse> RouterFunction<T> route(RequestPredicate predicate,
                                                                 HandlerFunction<T> handlerFunction)

RequestPredicate

RequestPredicate is another Java 8 FunctionalInterface which tests a given ServerRequest to be acceptable for this routing:

package org.springframework.web.reactive.function.server;
 ...
@FunctionalInterface
public interface RequestPredicate {
    boolean test(ServerRequest request);
 ...
}

Creating a RequestPredicate

RequestPredicates is another utility class which will help us to create a RequestPredicate quickly, for example following method will create the predicate to match a path pattern:

public static RequestPredicate path(String pattern)

Example

In following example, we are going to use Spring Boot 2.0.0.M5. We need to register our RouterFunction as a bean so that Spring WebFlux runtime can use it to handle HTTP requests.

Maven Dependencies

pom.xml

<project .....>
<modelVersion>4.0.0</modelVersion>
<groupId>com.logicbig.example</groupId>
<artifactId>spring-reactive-functional-helloworld</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M5</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>

<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.8.RELEASE</version>
</plugin>
</plugins>
</build>

</project>

Defining our HandlerFunction and RouterFunction

package com.logicbig.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.config.EnableWebFlux;
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;
import reactor.core.publisher.Mono;

@SpringBootApplication
@EnableWebFlux
public class ExampleApplication {

    @Bean
    RouterFunction<ServerResponse> helloRouterFunction() {
        RouterFunction<ServerResponse> routerFunction =
                RouterFunctions.route(RequestPredicates.path("/"),
                        serverRequest ->
                                ServerResponse.ok().body(Mono.just("Hello World!"), String.class));

        return routerFunction;
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(ExampleApplication.class);
    }
}

To make it more readable:

 @Bean
 RouterFunction<ServerResponse> helloRouterFunction() {
    HandlerFunction<ServerResponse> handlerFunction = serverRequest ->
            ServerResponse.ok().body(Mono.just("Hello World!"), String.class);

    RouterFunction<ServerResponse> routerFunction =
            RouterFunctions.route(RequestPredicates.path("/"), handlerFunction);

    return routerFunction;
 }

Grouping handler functions in classes

It is recommended to group related handler functions into handler or controller classes and use Java 8 method references in the RouterFunctions.

Rewriting our above example:

package com.logicbig.example;

import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

public class HelloHandler {
    public  Mono<ServerResponse> handleRequest(ServerRequest serverRequest) {
        return ServerResponse.ok().body(Mono.just("Hello World!"), String.class);
    }
}
@SpringBootApplication
@EnableWebFlux
public class ExampleApplication {

    @Bean
    HelloHandler helloHandler() {
        return new HelloHandler();
    }

    @Bean
    RouterFunction<ServerResponse> helloRouterFunction(HelloHandler helloHandler) {
        return RouterFunctions.route(RequestPredicates.path("/"),
                helloHandler::handleRequest);
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(ExampleApplication.class);
    }
}

Running:

To try examples, run spring-boot maven plugin (configured in pom.xml of example project below):

mvn spring-boot:run

We can also run the main class from our IDE.

Output

Example Project

Dependencies and Technologies Used :

  • spring-boot-starter-webflux 2.0.0.M5: Starter for building WebFlux applications using Spring Framework's Reactive Web support.
    Corresponding Spring version: 5.0.0.RELEASE
  • JDK 1.8
  • Maven 3.3.9

Spring WebFlux Reactive Functional Example Select All Download
  • spring-reactive-functional-helloworld
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also