0
votes

I am using Spring Cloud Gateway as an API-Gateway and also as a webserver hosting the static files (html/js/css) of an Vue.js SPA.

Preclaimer: I'm not able to change this (bad) architecture due to organisational constraints.

Currently, I'm using the default Vue Router hash mode, meaning client-side routing of the Vue app is accomplished via an URL hash like

http://hostname/#/route?parameter1=true

Because the Spring Cloud Gateway also acts as an Authentication gateway, it redirects to an OAuth2/OpenID SSO server, and by doing that, the route information of the URL hash is dropped after the redirect.

I'm trying to change this behaviour by switching to Vue Router's history mode, which would enable URLs like

http://hostname/route?parameter1=true

and therefore, routing information would "survive" SSO redirects.

To do that, Vue Router documentation includes some examples for additional Webserver configuration (like mod_rewrite for Apache etc.).

Sadly there is no example included for my very special case ;-)

I scanned the documentation of Spring Cloud Gateway, but didn't find a match for this case.

So in short:

Is it possible to configure Spring Cloud Gateway to match Vue Router's history mode requirements?

2

2 Answers

1
votes

You could fix it from the Spring Boot side. I mean, you could:

  1. Enable the Vue Router history mode like explained in the documentation here
  2. Build a Spring Boot forwarding controller like this:
        @RequestMapping(value = "{_:^(?!index\\.html|api).$}")
        public String redirectApi() {
            LOG.info("URL entered directly into the Browser, so we need to redirect...");
            return "forward:/";
        }

It basically forwards all routes to front end except: /, /index.html, /api, /api/**.

Here you can find a more detailed example.

0
votes

It can be done with a custom RouterFunction:

@Bean
public RouterFunction<ServerResponse> vueHistoryModeCatchAllRoute(
        @Value("classpath:/static/index.html") final Resource indexHtml) {
    HandlerFunction<ServerResponse> serveIndexHtmlFunction = request -> ok().contentType(MediaType.TEXT_HTML)
            .bodyValue(indexHtml);
    String firstApiSegmentExcludes = "api|actuator|js|img|css|fonts|favicon\\.ico";
    return route(GET("/{path:^(?!" + firstApiSegmentExcludes + ").*}"), serveIndexHtmlFunction)
            .and(route(GET("/{path:^(?!" + firstApiSegmentExcludes + ").*}/**"),
                    serveIndexHtmlFunction));
}

This will serve the index.html (containing the Vue.js app) for all requests, which do not match one of the excluded paths (firstApiSegmentExcludes).