Spring – @RequestMapping for empty servlet path is not working

spring, spring-mvc

I am using Spring 3.2.2 Web MVC with default annotation mapping. I use a servlet mapping like this:

<servlet>    <servlet-name>profil</servlet-name>    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    <init-param>        <param-name>contextClass</param-name>        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>    </init-param>    <init-param>        <param-name>contextConfigLocation</param-name>        <param-value>de.kicktipp.web.config.servlets.ProfilServletConfig</param-value>    </init-param>    <load-on-startup>1</load-on-startup></servlet><servlet-mapping>    <servlet-name>profil</servlet-name>    <url-pattern>/info/profil/*</url-pattern></servlet-mapping>

This is our servlet configuration:

@[email protected]("de.kicktipp.controller")@EnableWebMvcpublic class ProfilServletConfig extends WebMvcConfigurerAdapter{    @Override    public void addInterceptors ( InterceptorRegistry registry )    {       // we add a few interceptors here    }    @Bean    public DefaultRequestToViewNameTranslator viewNameTranslator ( )    {        DefaultRequestToViewNameTranslator defaultRequestToViewNameTranslator = new DefaultRequestToViewNameTranslator();        defaultRequestToViewNameTranslator.setStripExtension(false);        defaultRequestToViewNameTranslator.setAlwaysUseFullPath(false);        defaultRequestToViewNameTranslator.setPrefix("profil/");        return defaultRequestToViewNameTranslator;    }}

The wildcard matching is important because we want to match many urls with this pattern like /info/profil/page1, /info/profil/page2 and so on.

When I want to match the "base" URL /info/profil without a trailing slash it is picked up by the servlet "profil".

Now I have tried three controller methods to match /info/profil with a handler method:

@RequestMapping("/")protected void get1 () {}@RequestMapping("")protected void get2 () {}@RequestMapping("/info/profil")protected void get3 () {}

Only the last one works. This is because UrlPathHelper#getLookupPathForRequest(javax.servlet.http.HttpServletRequest) returns the full path within application if the path within the servlet is an empty string:

public String getLookupPathForRequest(HttpServletRequest request) {    // Always use full path within current servlet context?    if (this.alwaysUseFullPath) {        return getPathWithinApplication(request);    }    // Else, use path within current servlet mapping if applicable    String rest = getPathWithinServletMapping(request);    if (!"".equals(rest)) {        return rest;    }    else {        return getPathWithinApplication(request);    }}

For the request to "/info/profil/" the method will return "/", but for "/info/profil" (without trailing slash) it will return "/info/profil" because the rest variable is empty string and teherfore the method returns the pathWithinApplication.

Other paths are usually matched against the path inside the servlet mapping (as alwaysUseFullPath defaults to false). But the "root" path is matched against the full path inside the application (like it always does when alwaysUseFullPath is true).

Why is it like this? Why is spring not trying match the empty string but instead tries to match the path within the application?

See spring issue here https://jira.springsource.org/browse/SPR-10491

Best Solution

try adding

@RequestMapping(method = RequestMethod.GET) public String list() { return "redirect:/strategy/list"; }

the result:

    @RequestMapping(value = "/strategy")    public class StrategyController {    static Logger logger = LoggerFactory.getLogger(StrategyController.class);    @Autowired    private StrategyService strategyService;    @Autowired    private MessageSource messageSource;    @RequestMapping(method = RequestMethod.GET)    public String list() {        return "redirect:/strategy/list";    }       @RequestMapping(value = {"/", "/list"}, method = RequestMethod.GET)    public String listOfStrategies(Model model) {        logger.info("IN: Strategy/list-GET");        List<Strategy> strategies = strategyService.getStrategies();        model.addAttribute("strategies", strategies);        // if there was an error in /add, we do not want to overwrite        // the existing strategy object containing the errors.        if (!model.containsAttribute("strategy")) {            logger.info("Adding Strategy object to model");            Strategy strategy = new Strategy();            model.addAttribute("strategy", strategy);        }        return "strategy-list";    }  

** credits:

Advanced @RequestMapping tricks – Controller root and URI Template

and

CRUD tutorial by dtr-trading.blogspot.it