25

spring-boot-starter-webflux (Spring Boot v2.0.0.M2) is already configured like in a spring-boot-starter-web to serve static content in the static folder in resources. But it doesn't forward to index.html. In Spring MVC it is possible to configure like this:

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/").setViewName("forward:/index.html");
}

How to do it in Spring Webflux?

Ihor Rybak
  • 3,091
  • 2
  • 25
  • 32

5 Answers5

36

Do it in WebFilter:

@Component
public class CustomWebFilter implements WebFilter {
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    if (exchange.getRequest().getURI().getPath().equals("/")) {
        return chain.filter(exchange.mutate().request(exchange.getRequest().mutate().path("/index.html").build()).build());
    }

    return chain.filter(exchange);
  }
}
Ihor Rybak
  • 3,091
  • 2
  • 25
  • 32
Alphaone
  • 570
  • 5
  • 6
  • This is ok but for dynamic requirements it is not that ok – kakabali May 18 '20 at 07:32
  • @kakabali depends what you mean dynamic, but it works just fine with things `/blog/{id}/comments` for example (that being a dynamic route). – Chris Aug 19 '20 at 12:00
13
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;

@Bean
public RouterFunction<ServerResponse> indexRouter(@Value("classpath:/static/index.html") final Resource indexHtml) {
return route(GET("/"), request -> ok().contentType(MediaType.TEXT_HTML).bodyValue(indexHtml));
}
stergipe
  • 125
  • 1
  • 1
  • 11
user2071703
  • 305
  • 3
  • 8
9

There's a ticket in the Spring Boot tracker for this.

Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
3

The same by using WebFlux Kotlin DSL:

@Bean
open fun indexRouter(): RouterFunction<ServerResponse> {
    val redirectToIndex =
            ServerResponse
                    .temporaryRedirect(URI("/index.html"))
                    .build()

    return router {
        GET("/") {
            redirectToIndex // also you can create request here
        }
    }
}
Manushin Igor
  • 3,398
  • 1
  • 26
  • 40
  • How do I do it by forwarding instead of redirect? – lfmunoz Jun 01 '20 at 18:19
  • @lfmunoz, I didn't find Forward http status. Probably, there is pre-built function. However you can construct your own response with construction like this: ```ServerResponse .status(HttpStatus.I_AM_A_TEAPOT) .header("my-header", "2342")```, just put right status and right headers there – Manushin Igor Jun 01 '20 at 18:33
0

If you came here like me looking for a solution that works for dynamic routes as a fallback solution for SPA use cases, there is an open spring issue here

The following WebExceptionHandler solved it for me

@Component
@Order(-2)
public class HtmlRequestNotFoundHandler implements WebExceptionHandler {

    private final DispatcherHandler dispatcherHandler;

    private final RequestPredicate PREDICATE = RequestPredicates.accept(MediaType.TEXT_HTML);

    public HtmlRequestNotFoundHandler(DispatcherHandler dispatcherHandler) {
        this.dispatcherHandler = dispatcherHandler;
    }

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable throwable) {
        if (
            isNotFoundAndShouldBeForwarded(exchange, throwable)
        ) {
            var forwardRequest = exchange.mutate().request(it -> it.path("/index.html"));
            return dispatcherHandler.handle(forwardRequest.build());
        }
        return Mono.error(throwable);
    }

    private boolean isNotFoundAndShouldBeForwarded(ServerWebExchange exchange, Throwable throwable) {
        if (throwable instanceof ResponseStatusException
            && ((ResponseStatusException) throwable).getStatusCode() == HttpStatus.NOT_FOUND
        ) {

            var serverRequest = ServerRequest.create(exchange, Collections.emptyList());
            return PREDICATE.test(serverRequest);
        }

        return false;
    }

}
Goatfryed
  • 118
  • 6