1

My problem is how to configure a Spring MVC application to allow at the same time

  • application should serve static resources (css, js, images ...)
  • the root url (http://my.host.org/webb_app/) should be served by a Spring controller

I've already read How to handle static content in Spring MVC?, Using Spring, mapping to root in web.xml, static resources aren't found and Tomcat serving static resources on Spring MVC app. All give working solutions, and until recently, I used any them as a cooking recipe until the application works more or less acceptably. All that without references nor a clear understanding of the why and how it finally worked.

So the question is : what at the different ways of configuring a Spring MVC application for this requirement, what are their drawbacks and what is the rationale behind them.

Community
  • 1
  • 1
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252

1 Answers1

2

I will begin with a preliminary remark about how DefaultServlet works. According to Servlet 3.0 specifications, containers generally provide a default servlet, that has lowest priority and serves static context. The mapping / is the implicit mapping for this default servlet.

Now for the solutions :

Map spring controllers to a sub-hierarchy

That is the easiest solution : you map Spring DispatcherServlet to /pages, or to /pages and /api for example. The default servlet will then serve all other URLs (including root). To serve root controller, you can map a controller to /home (for example) and have a /index.jsp containing <jsp:forward page="/home"/> - this is a method of current use in other frameworks using extension mapping such as Struts (*.do for old Struts1).

Drawbacks : having url stating with /pages is not very nice.

Map resources to a sub-hierarchy

This solution is highly used in the referenced pages. Spring DispatcherServlet is mapped to /* and so gets all the requests (unless a more specific mapping exists). To serve static resources, you just declare a ResourceHttpRequestHandler, using in XML :

<mvc:resources mapping="/resources/**" location="/public-resources/"/>

or in java configuration :

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/");
    }
}

This works very fine, and you can map a Spring controller to / directly.

Drawbacks : you cannot serve static resources that would be directly under root context.

Map DispatcherServlet as the default servlet

Mapping Spring DispatcherServlet to / is in fact replacing the default servlet from the container to process all not already processed URLs. With this mapping, Spring can fallback to the original default servlet for URLs not mapped to controllers. For that to work, you have to configure a DefaultServletHttpRequestHandler with a URL mapping of "/**" and the lowest priority. You do it using XML :

<mvc:default-servlet-handler/>

or in java configuration :

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

}

That way, DispatcherServlet normally calls all controllers, and have the original default servlet to serve static (not mapped) resources. Unfortunately, this does not work for the root URL, and you must use the <jsp:forward page="..."/> trick like for first solution.

Drawbacks :

  • cannot directly map root URL and need the index.jsp <jsp:forward page="..."/> trick
  • As Spring as replaced original container default servlet, it must call it by name. It works for common containers (including Tomcat, Jetty, GlassFish, JBoss, Resin, WebLogic, and WebSphere), or you can also give the name for default servlet as an attribute in XML config (<mvc:default-servlet-handler default-servlet-name="customDefaultServlet"/>) or as a parameter if java configuration: configurer.enable("customDefaultServlet");

References : Spring Reference Manual / Web MVC framework / Serving of Resources

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • There's another thing you can do: map the `DispatcherServlet` to `/` (replaces the default servlet), and `registry.addResourceHandler("/**").addResourceLocations("/")`. Everything is available from the root, however I'm not sure how correct it is. Can you comment on this? Thanks. – bigstones Jan 10 '16 at 21:14