Spring multiple path variables
Asked Answered
A

5

14

In my Spring controller, I try to get 3 path variables:

@RequestMapping("{language}/{country}/{term}/catalogue") - @PathVariable String language, @PathVariable String country, @PathVariable String term

Unfortunately this will not be recognized by the servlet.

There are ways to bind the URI, e.g.
@RequestMapping("**/catalogue") and also @RequestMapping("{language}/{country}/catalogue") will work, but with a third path variable the it stops working.

The controller itself is also mapped to a specific path.

Is there a limit for path variables? Is it possible that other wildcards (e.g. @RequestMapping("**")) will be higher evaluated? e.g. 2 wildcards more specific than 3 defined values. But wildcards should be the last matching option in praxis.

Regarding the appearing error:
First, with the wildcard mappings, they will be matched. When I disable the wildcard mappings a org.springframework.web.HttpRequestMethodNotSupportedException error is thrown.

15:42:53,881  DEBUG [http-bio-18091-exec-31] (org.springframework.web.servlet.DispatcherServlet) - Handler execution resulted in exception - forwarding to resolved error view: ModelAndView: reference to view with name 'errors/exception'; model is null
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodResolver.resolveHandlerMethod(AnnotationMethodHandlerAdapter.java:665)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:431)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at [device detection filter]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)

The Controller method:

@RequestMapping(value = "{language}/{country}/{term}/catalogue", method = RequestMethod.GET)
public ModelAndView catalogue(HttpServletRequest request, HttpServletResponse response, @PathVariable("language") String language, @PathVariable("country") String country, @PathVariable("term") String term, @RequestParam(value = "d", defaultValue = "") String device, @RequestParam(value = "embedded", defaultValue = "false") String embedded, @RequestParam(value = "id", defaultValue = "") String idString, @RequestParam(value = "nr", defaultValue = "") String nr) {

As ask, here are all RequestMappings from the controller (Sorry, I can't post the complete code from the controller here):

@Controller
@RequestMapping("xyz/")
public class Controller {

@RequestMapping(value = "{language}/{country}/{term}/catalogue", method = RequestMethod.GET)

@RequestMapping("**")

@RequestMapping("{language}/{country}/product")

@RequestMapping("{language}/{country}/product-detail")

@RequestMapping("{language}/{country}/product-search")

@RequestMapping("{language}/{country}/dealer-search")

@RequestMapping("{language}/{country}/product-finder")

@RequestMapping("{language}/{country}/table")

@RequestMapping("**/languages")

@RequestMapping("**/chooseLanguages")
}    

Thanks for help.

Apostle answered 16/1, 2015 at 13:48 Comment(14)
Please provide more context, i.e. the controller class' @RequestMapping and other methods' @RequestMappings involved.Benzocaine
@Controller @RequestMapping("xyz")Apostle
What about the error? When does it occur? Are you getting a 404 response from Spring when you send the request or is it that the application didn't manage to start?Benzocaine
vote up for the error thats occuring, maybe a stack trace?Kuehn
The Controller annotation is: @Controller @RequestMapping("xyz"). There are also 2 Wildcard methods, one in the root controller and one in the "xyz" controller. Spring version is 3.1.0.RELEASE. Other Mappings with wildcards have a static value at the end (e.g. **/example)Apostle
The webapp starts, only the mentioned method can't be accessed and is matched by other methods or, if the wildcards are disabled, the error page is shown and the stack trace i've edited to the question occures.Apostle
Can you post your code, along with any other methods in the same class?Kuehn
@user3679981 Please read Spring docs (link here). There it says When a URL matches multiple patterns, a sort is used to find the most specific match. A pattern with a lower count of URI variables and wild cards is considered more specific. So in your case, your application is behaving as expected.Benzocaine
@Magnamag as written above, even when the wildcard mappings are disabled, the pattern won't be matched.Apostle
@user3679981 You're just missing a / (dash) at the end of xyz in the controller's @RequestMapping.Benzocaine
(I meant a slash, not a dash :)Benzocaine
@Magnamag Thanks for the hint. I've edited this in the code but it still don't work.Apostle
@Apostle what's the exact method's signature?Benzocaine
@Magnamag I've edited my post with the complete methode signatureApostle
N
1

This may be a bug (fixed in Spring 4.1): check out SPR-6741.

As described in the issue, you have in the same controller:

  • a mapping with 3 path variables
  • a "fallback" mapping /**
Nausea answered 17/1, 2015 at 9:56 Comment(0)
W
8

Try this. Don't forget the ("lang") in the path variable declaration in your method parameter

@RequestMapping(value = "/{lang}/{count}/{term}", method=RequestMethod.GET)
public ResponseEntity<?> getSomething(@PathVariable("lang") String lang, @PathVariable("count") String count, @PathVariable("term") String term) {
Waac answered 16/1, 2015 at 15:4 Comment(1)
Can you explain why this is a good idea, and what the relevant difference is with what the OP is already doing? Are you saying that "language" is somehow not a good name for a path variable?Microvolt
B
1

First of all, you're missing a / (slash) at the end of xyz in the controller's @RequestMapping (or a / at the beggining of every method's @RequestMapping). This is the main reason for your 3 path variable method being never invoked.

@RequestMapping(value = "{language}/{country}/{term}/catalogue", method = RequestMethod.GET)

However, once you solve that, I think you might have another problem. According to Spring docs (link here):

When a URL matches multiple patterns, a sort is used to find the most specific match.

A pattern with a lower count of URI variables and wild cards is considered more specific.

This means that when you enable wildcard matching, even if you added the / at the end of your controller's @RequestMapping, your 3 path variable method wouldn't be invoked, because the one with wildcards is considered more specific by Spring.

Please check all @RequestMappings for missing slashes. I advise you to always begin with a slash, this way it's easier to distinguish URL mappings in your code.

Benzocaine answered 16/1, 2015 at 18:8 Comment(0)
N
1

This may be a bug (fixed in Spring 4.1): check out SPR-6741.

As described in the issue, you have in the same controller:

  • a mapping with 3 path variables
  • a "fallback" mapping /**
Nausea answered 17/1, 2015 at 9:56 Comment(0)
D
0

Just put this in your code it would be work nice :

@GetMapping(value = "{language}/{country}/{term}/catalogue",produces = {"application/json"})
public @ResponseBody String test(@PathVariable String language,@PathVariable String country,@PathVariable String term) {

        return language+"\t"+country+"\t"+term
    }

You'd pass in: http://localhost:8080/api/Français/France/fr/catalogue

Difference answered 10/5, 2020 at 3:46 Comment(0)
S
-1

Try this:

@RequestMapping(value = "/{lang}/{count}/{term}", method=RequestMethod.GET)
public ResponseEntity<?> getSomething(@PathVariable("lang") String lang, @PathVariable("count") String count, @PathVariable("term") String term) {
   // Your code goes here.
}
Smithson answered 17/12, 2016 at 15:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.