Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

map all requests instead of specific pattern in Spring Boot

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

like image 234
kojot Avatar asked Feb 01 '18 14:02

kojot


People also ask

What does @RequestMapping do in spring?

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 .

What is @RequestMapping annotation in spring boot?

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.

How do you specify prefix for all controllers in spring boot?

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(){ // ... } }

How do I request all parameters in spring?

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.


3 Answers

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.

like image 62
kojot Avatar answered Oct 20 '22 01:10

kojot


You can add a default handler method

  @RequestMapping()
  String defaultHandler() { 
    return "This is a default method";
  }
like image 35
Ali Zhagparov Avatar answered Oct 20 '22 01:10

Ali Zhagparov


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:/";
    }
}
like image 22
JDB Avatar answered Oct 20 '22 01:10

JDB