Paulius Matulionis
Paulius Matulionis

Reputation: 23413

Matching anything in Spring RequestMapping

On Spring MVC rest service I am having issues trying to match anything that is beyond my configured RequestMapping value.

So for e.g. I have this:

@RequestMapping(value = "{configKey}/{arguments:.*}", method = RequestMethod.GET)

Which says that match anything that is beyond the second path variable. The problem is that this e.g. works ok with:

get("/test/document")

while this ends up with 404:

get("/test/document/download")

It is weird that Spring can't handle this regex. I actually tried a lot of solutions, but none of them worked.

Previously I had this configuration on JAX-RS:

@Path("/{configKey}/{arguments:.*}")

And everything was good, but now I am migrating and having this issue.

Does anyone know what's going on and how to fix this?

EDIT:

Adding {configKey}/** - doesn't work

Adding {configKey}/{arguments}/** works, but for e.g. if I call:

get("/test/document/download") I get only test as my config key and document as arguments. In the arguments I expect to get all what's beyond the {configKey}. Which can be anything for e.g. it should work in any case:

get("/test/document")
get("/test/document/download")
get("/test/document/download/1")
get("/test/document/download/1/2")
get("/test/whatever/xxx/1/2/etc")

Which was working with config for JAX-RS: @Path("/{configKey}/{arguments:.*}")

Upvotes: 6

Views: 12809

Answers (4)

Mike DeMille
Mike DeMille

Reputation: 441

For anyone that lands here looking for an answer, but realizes that the current answers are from 2015, this has been improved with newer versions of Spring because of the PathPattern.

https://spring.io/blog/2020/06/30/url-matching-with-pathpattern-in-spring-mvc

PathPattern is compatible with AntPathMatcher syntax except for the following:

Support for additional syntax to match and capture 0 or more path segments at the end, e.g. "/foo/{*spring}". This is useful as a catch-all pattern in REST APIs with access to the captured path segments through a @PathVariable.

Support for "**" for multi-segment matching is only allowed at the end of a pattern. This helps to eliminate most causes of ambiguity when choosing the closest match for a given request.

In particular, a @GetMapping(path = "/foo/{*spring}") did the trick for me (a @RequestMapping would work too), which injects a @PathVariable String spring to your method. So if I called the API with GET /foo/1/2/3, the String spring would equal /1/2/3 (yes, with the leading slash)

Upvotes: 0

Paulius Matulionis
Paulius Matulionis

Reputation: 23413

I found a workaround to it, it's not the permanent solution, I think it's a bug in Spring and I raised a Jira, but until it's fixed here it is:

I had to define my request mapping like this:

@RequestMapping(value = "{configKey}/**", method = RequestMethod.GET)

So basically match all what's after first variable in a path.

Then:

String arguments = pathMatcher.extractPathWithinPattern(
        request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString(),
        request.getPathInfo());

Where a pathMatcher is an instance of AntPathMatcher used by Spring.

So now calling HTTP GET on for e.g. this path:

get("/test/leaderboard/user/mq/frankie1")

I have:

configKey = test
arguments = leaderboard/user/mq/frankie1

Upvotes: 0

Adi Sembiring
Adi Sembiring

Reputation: 5936

Spring Use AntPathMatcher, The mapping matches URLs using the following rules:

1. ? matches one character
2. * matches zero or more characters
3. ** matches zero or more 'directories' in a path

This is how I configure request mapping url, I've test in my computer, and it works and You can custom based on your needs.

@RequestMapping(value = "/new-ajax/**", method = RequestMethod.GET)

Test case

/new-ajax/document/1
/new-ajax/document/download/1
/new-ajax/document/download/1/2
/new-ajax/test/whatever/xxx/1/2/etc

Upvotes: -1

Master Slave
Master Slave

Reputation: 28569

The following mapping should work for you

@RequestMapping(value = "{configKey}/**", method = RequestMethod.GET)

This mapping is known as default mapping pattern.

Upvotes: 2

Related Questions