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);
}
}
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();
}
}
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 @Controller
s.
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:
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");
}
}
<?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>
<?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 listinghttp://localhost/myapp/ui
and http://localhost/myapp/ui/
-> index.jsphttp://localhost/myapp/ui/other
and http://localhost/myapp/ui/other/
-> other.jspHope this helps!
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With