11

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.

ישו אוהב אותך
  • 28,609
  • 11
  • 78
  • 96
Noctem
  • 123
  • 1
  • 1
  • 5
  • Please provide more context, i.e. the controller class' `@RequestMapping` and other methods' `@RequestMapping`s involved. – fps Jan 16 '15 at 14:08
  • @Controller @RequestMapping("xyz") – Noctem Jan 16 '15 at 14:20
  • 2
    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? – fps Jan 16 '15 at 14:26
  • vote up for the error thats occuring, maybe a stack trace? – ConMan Jan 16 '15 at 14:34
  • 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) – Noctem Jan 16 '15 at 14:37
  • 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. – Noctem Jan 16 '15 at 14:58
  • Can you post your code, along with any other methods in the same class? – ConMan Jan 16 '15 at 15:04
  • @user3679981 Please read Spring docs (link [here](http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-requestmapping-pattern-comparison)). 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. – fps Jan 16 '15 at 15:33
  • @Magnamag as written above, even when the wildcard mappings are disabled, the pattern won't be matched. – Noctem Jan 16 '15 at 15:44
  • @user3679981 You're just missing a `/` (dash) at the end of `xyz` in the controller's `@RequestMapping`. – fps Jan 16 '15 at 15:44
  • (I meant a slash, not a dash :) – fps Jan 16 '15 at 15:56
  • @Magnamag Thanks for the hint. I've edited this in the code but it still don't work. – Noctem Jan 16 '15 at 16:02
  • @Noctem what's the exact method's signature? – fps Jan 16 '15 at 16:06
  • @Magnamag I've edited my post with the complete methode signature – Noctem Jan 16 '15 at 16:10

5 Answers5

7

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) {
Susie
  • 5,038
  • 10
  • 53
  • 74
  • 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? – Erwin Bolwidt Jan 16 '15 at 15:16
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.

Community
  • 1
  • 1
fps
  • 33,623
  • 8
  • 55
  • 110
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 /**
Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
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

aminography
  • 21,986
  • 13
  • 70
  • 74
-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.
}
Aurasphere
  • 3,841
  • 12
  • 44
  • 71
AJEET SINGH
  • 11
  • 1
  • 4