3

I am building a Vue.JS & Spring Boot app which I am running through a docker container. The dist folder for Vue is copied to resources/public path and served through the Spring Boot service.

I have set up routes using vue router, but all of these routes return 404 - Not found when entered directly into the browser (but work fine when accesses through the Vue app).

The vue router:

export default new Router({
  mode: 'history',
  routes: [{
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/result',
      name: 'result',
      component: Result,
      props: true
    },
    {
      path: '/result/:userid',
      name: 'autoResult',
      component: Result,
      props: true
    }
  ]
})

I need the /result/userid to not return a 404 - instead it should get the userid & render the result page. Is this possible to get to work?

Another thing I want to do is to redirect all 404-pages that are not mapped to any api / vue page to return to the start page. I have tried using the spring boot implements ErrorController but I cannot get the redirect to work.

Edit:

I tried adding the following Controller Advice:

@ControllerAdvice
public class WebConfig {

    @ExceptionHandler(NoHandlerFoundException.class)
    public String renderDefaultPage(NoHandlerFoundException e) {
        return "classpath:public/index.html";
    }

}

And the following properties:

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
spring.mvc.static-path-pattern=/static/**
spring.resources.static-locations=classpath:public/static/

But when I try to access the frontpage now (or any other URL) i get the stackOverflowException and the server starts doing an infinite loop saying this:

2019-03-05 13:26:24.298  WARN 22044 --- [nio-8080-exec-1] o.s.web.servlet.PageNotFound             : No mapping for GET /classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/classpath:public/index.html
Ben Cass
  • 133
  • 2
  • 7
user2868900
  • 721
  • 3
  • 9
  • 29

3 Answers3

3

Solved with the following code:

@Controller
public class RoutesController implements ErrorController {
    private static final String PATH = "/error";

    @RequestMapping(value = PATH)
    public String error() {
        return "forward:/";
    }

    @Override
    public String getErrorPath() {
        return PATH;
    }
}
user2868900
  • 721
  • 3
  • 9
  • 29
1

What's happening in your case is that Spring boot is taking the requests and, because nothing is mapped to the URL is giving you a 404. What you want to happen instead is to allow your Vue.js app to handle the unmapped URL's (IE, redirect any unmapped URL's to your index.html).

The first thing you need to do is adding this to your routes configuration in your router:

export default new Router({
    mode: 'history',
    routes: [{
        path: '/',
        name: 'home',
        component: Home
    },
    {
        path: '/result',
        name: 'result',
        component: Result,
        props: true
    },
    {
        path: '/result/:userid',
        name: 'autoResult',
        component: Result,
        props: true
    },
    {
        path: '*',
        component: NotFound
    }
  ]
})

Here we added an extra route as the last path (because routes are matched sequentially) that renders a component.

After that, you need to make Spring boot redirect every unmatched request to index.html, in order to do that, you want spring to throw an exception when it finds and unmapped route and in the handler of the exception redirect to your index.html.

First, add this line to your application.properties:

spring.mvc.throw-exception-if-no-handler-found=true

And add a ControllerAdvice to handle the thrown Exception, like this:

//In some controller or inside a @ControllerAdvice annotated class
@ExceptionHandler(NoHandlerFoundException.class)
String noHandlerFound(NoHandlerFoundException ex){
    return "classpath:index.html";
}

Here you can find a little bit more info on making Spring boot redirect unmapped requests to your index.html

  • Hi, I tried this but only receive errors. I updated the original post to show the error. I can't seem to get it to work – do you see what could be wrong? The path for my static files is `target->classes->public-> index.html ->/static/css /static/js` – user2868900 Mar 05 '19 at 12:31
0

Unfortunately none of the above answers worked for me, luckily I've found this answer in another thread: https://stackoverflow.com/a/59290035/1254782

It is simple and short and works like a charm for my use case, which might slightly differ from the original question, but I think it is quite similar. It might be useful for someone looking for serving vuejs SPA from spring boot backend.

Sandor Farkas
  • 153
  • 2
  • 11