How do I map Spring MVC controller to a uri with a

2020-01-31 03:18发布

I have a Spring Controller with several RequestMappings for different URIs. My servlet is "ui". The servlet's base URI only works with a trailing slash. I would like my users to not have to enter the trailing slash.

This URI works:

http://localhost/myapp/ui/

This one does not:

http://localhost/myapp/ui

It gives me a HTTP Status 404 message.

The servlet and mapping from my web.xml are:

<servlet>
    <servlet-name>ui</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>ui</servlet-name>
    <url-pattern>/ui/*</url-pattern>
</servlet-mapping>    

My Controller:

@Controller
public class UiRootController {

    @RequestMapping(value={"","/"})
    public ModelAndView mainPage() { 
        DataModel model = initModel();
        model.setView("intro");     
        return new ModelAndView("main", "model", model);
    }

    @RequestMapping(value={"/other"})
    public ModelAndView otherPage() { 
        DataModel model = initModel();
        model.setView("otherPage");     
        return new ModelAndView("other", "model", model);
    }

}

6条回答
太酷不给撩
2楼-- · 2020-01-31 03:38

If your web application exists in the web server's webapps directory, for example webapps/myapp/ then the root of this application context can be accessed at http://localhost:8080/myapp/ assuming the default Tomcat port. This should work with or without the trailing slash, I think by default - certainly that is the case in Jetty v8.1.5

Once you hit /myapp the Spring DispatcherServlet takes over, routing requests to the <servlet-name> as configured in your web.xml, which in your case is /ui/*.

The DispatcherServlet then routes all requests from http://localhost/myapp/ui/ to the @Controllers.

In the Controller itself you can use @RequestMapping(value = "/*") for the mainPage() method, which will result in both http://localhost/myapp/ui/ and http://localhost/myapp/ui being routed to mainPage().

Note: you should also be using Spring >= v3.0.3 due to SPR-7064

For completeness, here are the files I tested this with:

src/main/java/controllers/UIRootController.java

package controllers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class UiRootController {
  @RequestMapping(value = "/*")
  public ModelAndView mainPage() {
    return new ModelAndView("index");
  }

  @RequestMapping(value={"/other"})
  public ModelAndView otherPage() {
    return new ModelAndView("other");
  }
}

WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0" metadata-complete="false">
  <servlet>
    <servlet-name>ui</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <!-- spring automatically discovers /WEB-INF/<servlet-name>-servlet.xml -->
  </servlet>

  <servlet-mapping>
    <servlet-name>ui</servlet-name>
    <url-pattern>/ui/*</url-pattern>
  </servlet-mapping>
</web-app>

WEB-INF/ui-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:component-scan base-package="controllers" />

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
  p:order="2"
  p:viewClass="org.springframework.web.servlet.view.JstlView"
  p:prefix="/WEB-INF/views/"
  p:suffix=".jsp"/>
</beans>

And also 2 JSP files at WEB-INF/views/index.jsp and WEB-INF/views/other.jsp.

Result:

  • http://localhost/myapp/ -> directory listing
  • http://localhost/myapp/ui and http://localhost/myapp/ui/ -> index.jsp
  • http://localhost/myapp/ui/other and http://localhost/myapp/ui/other/ -> other.jsp

Hope this helps!

查看更多
三岁会撩人
3楼-- · 2020-01-31 03:40

I eventually added a new RequestMapping to redirect the /ui requests to /ui/. Also removed the empty string mapping from the mainPage's RequestMapping. No edit required to web.xml.

Ended up with something like this in my controller:

    @RequestMapping(value="/ui")
    public ModelAndView redirectToMainPage() {
        return new ModelAndView("redirect:/ui/");
    }

    @RequestMapping(value="/")
    public ModelAndView mainPage() { 
        DataModel model = initModel();
        model.setView("intro");     
        return new ModelAndView("main", "model", model);
    }

    @RequestMapping(value={"/other"})
    public ModelAndView otherPage() { 
        DataModel model = initModel();
        model.setView("otherPage");     
        return new ModelAndView("other", "model", model);
    }

Now the URL http://myhost/myapp/ui redirects to http://myhost/myapp/ui/ and then my controller displays the introductory page.

查看更多
一夜七次
4楼-- · 2020-01-31 03:41

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

查看更多
霸刀☆藐视天下
5楼-- · 2020-01-31 03:43

PathMatchConfigurer api allows you to configure various settings related to URL mapping and path matching. As per the latest version of spring, trail path matching is enabled by default. For customization, check the below example.

For Java-based configuration

@Configuration
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseTrailingSlashMatch(true);
    }
}

For XML-based configuration

<mvc:annotation-driven>
    <mvc:path-matching trailing-slash="true"/>
</mvc:annotation-driven>

For @RequestMapping("/foo"), if trailing slash match set to false, example.com/foo/ != example.com/foo and if it's set to true (default), example.com/foo/ == example.com/foo

Cheers!

查看更多
老娘就宠你
6楼-- · 2020-01-31 03:53

Using Springboot, my app could reply both with and without trailing slash by setting @RequestMapping's "value" option to the empty string:

@RestController
@RequestMapping("/some")
public class SomeController {
//                  value = "/" (default) ,
//                  would limit valid url to that with trailing slash.
    @RequestMapping(value = "", method = RequestMethod.GET)
    public Collection<Student> getAllStudents() {
        String msg = "getting all Students";
        out.println(msg);
        return StudentService.getAllStudents();
    }
}
查看更多
地球回转人心会变
7楼-- · 2020-01-31 04:03

Another solution I found is to not give the request mapping for mainPage() a value:

@RequestMapping
public ModelAndView mainPage() { 
    DataModel model = initModel();
    model.setView("intro");     
    return new ModelAndView("main", "model", model);
}
查看更多
登录 后发表回答