spring-bootspring-webfluxstatic-content

Serve static content with webflux and multiple wildcards


I want to serve react app with routes from Spring Boot with WebFlux and functional routing.

I'm trying to implement following rules:

  1. There are few /api endpoints that should be routed with functions.

  2. Everything starting with the /static should serve static content.

  3. Everything else should serve index.html

I've added following function:

    @Bean
    RouterFunction<ServerResponse> routerFunction(Handler1 handler1,
                                                  Handler2 handler2) {
        HandlerFunction<ServerResponse> indexPage = (req) -> ServerResponse.ok().bodyValue(new ClassPathResource("public/index.html"));
        return RouterFunctions.route()
                              .GET("/api/route1", handler1)
                              .POST("/api/route2", handler2)
                              .resources("/static/**", new ClassPathResource("/public/**"))
                              .GET("/**", indexPage)
                              .build();
    }

API routes work fine, but when I try to get any static content I get index page.

Changing /** to / allows me get static content as well as index page by / route.

Based on the documentation route with static has should be checked first, but somehow it becomes overridden with the last wildcard route.

Router functions are evaluated in order: if the first route does not match, the second is evaluated, and so on. Therefore, it makes sense to declare more specific routes before general ones.

What am I missing?


Solution

  • Figured it out. ClassPathResource accepts path to location. No need for wildcards.

    With GET("/*", indexPage) static resources were served not using resources("/static/**", new ClassPathResource("/public/**")), but with the default webflux configuration spring.webflux.static-path-pattern.

    Correct router function declaration:

        @Bean
        RouterFunction<ServerResponse> routerFunction(Handler1 handler1,
                                                      Handler2 handler2) {
            HandlerFunction<ServerResponse> indexPage = (req) -> ServerResponse.ok().bodyValue(new ClassPathResource("public/index.html"));
            return RouterFunctions.route()
                                  .GET("/api/route1", handler1)
                                  .POST("/api/route2", handler2)
                                  .resources("/static/**", new ClassPathResource("/public/"))
                                  .GET("/**", indexPage)
                                  .build();
        }