Spring multiple path variables

52,040

Solution 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 /**

Solution 2

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) {
Share:
52,040
Admin
Author by

Admin

Updated on May 10, 2020

Comments

  • Admin
    Admin about 4 years

    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.

  • Erwin Bolwidt
    Erwin Bolwidt over 9 years
    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?