I have VueJS Single Page Application inside the Spring Boot aplication and I want to make vue-router to handle all of the requests instead of these ones which url starts with the /rest/**
.
So i wrote regular expression ^(?!/rest/).*
to match everything that doesn't start with the /rest/
and I try to make the request mapping like this:
@Controller
public class MainController {
@RequestMapping(value = "/{path:^(?!/rest/).*}")
public String forwardRequests() {
return "forward:/";
}
}
but it doesn't work. What I'm doing wrong?
EDIT
I have rest controller file:
@RestController
@RequestMapping(path = "/rest/project")
public class ProjectController {
@Autowired
private ProjectService projectService;
@GetMapping(value = "/{id}")
public Project getProjectById(@PathVariable Long id) {
return projectService.getProjectById(id);
}
}
which returns JSON with project details. I've got some pages like /login
and /projekty
so I need to forward them to index.html
to handle routing with vue. I know I can do something like this:
@Controller
public class MainController {
@RequestMapping(value = {"/login", "/projekty"}, method = RequestMethod.GET)
public String forwardRequests() {
return "forward:/";
}
}
and it works great, but I don't want to explicitly specify every urls, that's why I try to use regular expression. And I think I use wrong the path
variable, but I dont know how.
https://spring.io/guides/tutorials/spring-security-and-angular-js/ section Using "Natural" Routes
RequestMapping annotation is used to map web requests onto specific handler classes and/or handler methods. @RequestMapping can be applied to the controller class as well as methods. Today we will look into various usage of this annotation with example and other annotations @PathVariable and @RequestParam .
One of the most important annotations in spring is the @RequestMapping Annotation which is used to map HTTP requests to handler methods of MVC and REST controllers. In Spring MVC applications, the DispatcherServlet (Front Controller) is responsible for routing incoming HTTP requests to handler methods of controllers.
Then, we only need to apply the annotation to each controller we want to prefix: @Controller @ApiPrefixController public class SomeController { @RequestMapping("/users") @ReponseBody public String getAll(){ // ... } }
You can capture multiple parameters in a single Map object using the @RequestParam . If all the query parameters in the URL have unique key names, then you can follow this below approach. We basically capture the query parameters of key-value pairs into Java Map object.
Well, as @JDB said, the AntPathMatcher
compares the paths and finds the best match. So you don't have to be worry about the endpoints, which have specified paths, like my /rest
api.
You can add this:
@RequestMapping(value = "/{path:[^\\.]*}")
public String redirect() {
return "forward:/";
}
and all of your requests, eg. /login
, will be forwarded correctly to index.html
, where javascript can handle it.
The problem is with the URLs like /project/edit/33
. They doesn't match this regular expression, so you will see the Whitelabel Error Page 404 Not Found
. You can write this:
@RequestMapping(value = "/**/{path:[^\\.]*}")
public String redirect() {
return "forward:/";
}
and it works fine, but if you have security enabled, the /ouath/token
will return:
{
"timestamp":"2018-02-05T09:13:28.104+0000",
"status":405,
"error":"Method Not Allowed",
"exception":"org.springframework.web.HttpRequestMethodNotSupportedException",
"message":"Request method 'POST' not supported",
"path":"/oauth/token"
}
so you have to exclude oauth urls:
@RequestMapping(value = {"/{path:[^\\.]*}", "/**/{path:^(?!oauth).*}/{path:[^\\.]*}"}, method = RequestMethod.GET)
public String forward() {
return "forward:/";
}
and it works fine.
If you have problems with other endpointes provided by the framework, like /health
you can change regular expression to /**/{path:^(?!oauth|health).*}/{path:[^\\.]*}
It looks awfull, but it works. I belive someone will post here a better and cleaner solution.
You can add a default handler method
@RequestMapping()
String defaultHandler() {
return "This is a default method";
}
I don't know spring specifically, so I'm just making my best guess based on experience with other frameworks.
First, shouldn't the pattern exclude the forward slash, since you are already including it in your path?
/{path:^(?!rest/).*}
If that doesn't work, then all I can think is that the AntPathMatcher doesn't support lookaheads.
The typical pattern for this design would be to implement both the /rest/*
and /*
routes. In some frameworks this is just about ordering them correctly. According to Spring MVC's documentation, you may need to play around with the rules to make the /rest/*
route "more specific".
Here are the rules:
When multiple patterns match a URL, they must be compared to find the best match. This done via AntPathMatcher.getPatternComparator(String path) which looks for patterns that more specific.
A pattern is less specific if it has a lower count of URI variables and single wildcards counted as 1 and double wildcards counted as 2. Given an equal score, the longer pattern is chosen. Given the same score and length, the pattern with more URI variables than wildcards is chosen.
The default mapping pattern /** is excluded from scoring and always sorted last. Also prefix patterns such as /public/** are considered less specific than other pattern that don’t have double wildcards.
For the full details see AntPatternComparator in AntPathMatcher and also keep mind that the PathMatcher implementation used can be customized. See Path Matching in the configuration section.
So, based on my interpretation of those rules and your code, I think something along these lines would work:
// Your more specific patterns will take precedence
@RequestMapping(value = "/rest/**")
public String handleRestRequests() {
// forward this to your rest services
}
}
// Your less specific patterns will only apply if the more specific ones don't match
@RequestMapping(value = "/**")
public String forwardRequests() {
// Everything else
return "forward:/";
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With