How to set priority when mapping MVC Spring?

I have 2 spring controller mappings:

@Controller public class ContentController { @RequestMapping(value = "**/{content}.html") public String content(@PathVariable String content, Model model, HttpServletRequest request) { } } @Controller public class HomeController { @RequestMapping(value = "**/home") public String home(HttpServletRequest request, Model model) { } } 

The following URL matches two mappings: /home.html

However, I want to make sure that the "content" ALLWAYS display takes precedence over the "home" mapping. Is there any way to indicate this?

+6
source share
4 answers

I had a very similar problem lately when I had two types of ambiguous URLs:

  • One took a year, say /2012 , /2013 , etc. I created it using regular expression matching as follows: @RequestMapping("/{year:(?:19|20)\\d{2}}")
  • The other accepted a content identifier (aka slug). I created it using matching, for example @RequestMapping("/{slug}") .

It was important that the controller method "year" take precedence over "slug". Unfortunately (for me), Spring always used the slug controller method.

As Spring MVC prefers more specific mappings, I had to make my slug template less specific. Based on the Comparison of Path Patterns , I added a wild card to the bullet display: @RequestMapping("/{slug}**")

My controllers look like this, and now listByYear is called when the year ( /2012 , /1998 , etc.) is in the url.

 @Controller public class ContentController { @RequestMapping(value = "/{slug}**") public String content(@PathVariable("slug") final String slug) { return "content"; } } 

and

 @Controller public class IndexController { @RequestMapping("/{year:(?:19|20)\\d{2}}") public String listByYear() { return "list"; } } 

It’s not entirely accurate how to set the priority (which, in my opinion, would be an amazing feature), but give some kind of “pleasant” workaround and may be convenient in the future.

+4
source

The display of URLs is determined by the order in which the mappings are found.

So, you can force one controller to be created in the context of spring after another controller.

This can be done using the depend-on = "" attribute in the bean definition (in xml).

I'm not sure if it uses the first matching found or the last.

It’s also just a theory, I haven’t really tried it.

I see the log messages as follows:

 17:29:01.618 [main] INF S oswshSimpleUrlHandlerMapping - Mapped URL path [/resources/**] onto handler 'org.springframework.web.servlet.resource.ResourceHttpRequestHandler#0' 17:29:01.625 [main] INF S oswshSimpleUrlHandlerMapping - Mapped URL path [/**] onto handler 'org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0' 

It is worth having a lock in SimpleUrlHandlerMapping to see how it works.

0
source

Specific automatic ordering works now, as expected. (Using spring 5.0.10)

In AntPatternComparator

  /** * Compare two patterns to determine which should match first, ie which * is the most specific regarding the current path. * @return a negative integer, zero, or a positive integer as pattern1 is * more specific, equally specific, or less specific than pattern2. */ @Override public int compare(String pattern1, String pattern2) 

Which is used from AbstractUrlHandlerMapping::lookupHandler(String urlPath, HttpServletRequest request)

  Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath); if (!matchingPatterns.isEmpty()) { matchingPatterns.sort(patternComparator); if (logger.isDebugEnabled()) { logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns); } bestMatch = matchingPatterns.get(0); } 
0
source

This url (/home.html) does not match both mappings:

  @RequestMapping(value = "**/{content}.html") ... @RequestMapping(value = "**/home") … 

It just matches the first because it has the suffix ".html"!

-3
source

All Articles