It seems that the routes are not being correctly registered when runing with default Tomcat in version 2.4.0
Sample code:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.time.LocalDateTime;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
@SpringBootApplication
public class SpringBootFunctionalReactiveWithInterceptorsApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootFunctionalReactiveWithInterceptorsApplication.class, args);
}
@Configuration
static class RouterConfig {
@Bean
public RouterFunction<ServerResponse> timerRouterRunction(TestHandler testHandler) {
return route(GET("/time"), testHandler::getTime).andRoute(GET("/times"), testHandler::sendTimePerSec);
}
}
@Component
static class TestHandler {
public Mono<ServerResponse> getTime(ServerRequest serverRequest) {
return ok().contentType(MediaType.TEXT_PLAIN).body(Mono.just("Now is " + LocalDateTime.now()),
String.class);
}
public Mono<ServerResponse> sendTimePerSec(ServerRequest serverRequest) {
return ok().contentType(MediaType.TEXT_EVENT_STREAM)
.body(Flux.interval(Duration.ofSeconds(1)).map(l -> LocalDateTime.now().toString()), String.class);
}
}
}
Test case:
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SpringBootFunctionalReactiveWithInterceptorsApplicationTests {
@Autowired
private WebTestClient client;
@Test
void contextLoads() {
client.get().uri("/time").exchange().expectStatus().isOk();
}
@Test
void actuatorLoads() {
client.get().uri("/actuator").exchange().expectStatus().isOk();
}
}
contextLoads will fail while actuatorLoads will pass.
Comment From: DarekDan
It might be that spring-boot-starter-parent implies use of spring-boot-starter-web, which relies on Tomcat. After adding an exclusion, the service is operational.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>tomcat-embed-el</artifactId>
<groupId>org.apache.tomcat.embed</groupId>
</exclusion>
<exclusion>
<artifactId>tomcat-embed-core</artifactId>
<groupId>org.apache.tomcat.embed</groupId>
</exclusion>
<exclusion>
<artifactId>tomcat-embed-websocket</artifactId>
<groupId>org.apache.tomcat.embed</groupId>
</exclusion>
</exclusions>
</dependency>
Comment From: DarekDan
Is there a way to run WebFlux Reactive Functions on Tomcat? What configuration steps am I missing?
Comment From: bclozel
Could you show your entire POM file please?
The spring-boot-starter-web
dependency is for building Spring MVC applications. If on the classpath, Spring Boot will assume you're building one and that the WebFlux dependency is here for using the WebClient (this is documented in the reference documentation).
If you'd like to build a reactive web application, the spring-boot-starter-webflux
dependency should be enough. If you'd like to switch to Tomcat, adding spring-boot-starter-tomcat
should work.
Comment From: DarekDan
Here it is:
```
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>tomcat-embed-el</artifactId>
<groupId>org.apache.tomcat.embed</groupId>
</exclusion>
<exclusion>
<artifactId>tomcat-embed-core</artifactId>
<groupId>org.apache.tomcat.embed</groupId>
</exclusion>
<exclusion>
<artifactId>tomcat-embed-websocket</artifactId>
<groupId>org.apache.tomcat.embed</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-webtestclient</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
```
Comment From: snicoll
As Brian has indicated, if you bring both spring-boot-starter-web
and spring-boot-starter-webflux
your app runs in servlet mode. If you intend to use Webflux.fn in a reactive web application, only spring-boot-starter-webflux
is necessary.
See the documentation and this question to run webflux on Tomcat for more details.
Comment From: DarekDan
One small comment, initially I did not have the spring-boot-starter-web artifact, and only added it to remove Tomcat dependency.
Comment From: snicoll
You won't get Tomcat unless you add spring-boot-starter-web
.
Comment From: DarekDan
This was closed prematurely.
Comment From: DarekDan
I do get Tomcat with the above pom.xml without spring-boot-starter-web dependency explicitly mentioned. Try it. :)
Comment From: bclozel
spring-boot-starter-data-rest
is based on Spring MVC, and thus depends on spring-boot-starter-web
.
Comment From: DarekDan
Thank you!