Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are there 2 ways to handle static resources in Spring (addResourceHandlers and the container's Default Servlet")?

I am new to Spring. I noticed that when handling static resources, there are two options available:


Option 1:

If Spring's DispatcherServlet is mapped to / with the below code, which makes it the "Default Servlet", it's possible to map certain static resources to Spring handlers with RequestMapping annotation (overriding the AbstractAnnotationConfigDispatcherServletInitializer class):

@Override
protected String[] getServletMappings() {
    return new String[]{"/"};
}

Then we can still enable the container's "Default Servlet" to handle those static resources whose URL pattern is not covered by Spring request mapping (overriding the WebMvcConfigurerAdapter class):

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

This basically uses the servlet container's "Default Servlet" as the catch-all to handle all the static resources missed by Spring's DispatcherServlet.


Option 2:

(overriding the WebMvcConfigurerAdapter class)

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

  • Why are there two options?
  • What are the main differences between these approaches?
  • Are there any other options?

I usually take option 2 because I want to stick to Spring, but I know that's not a strong reason.


Some reference related to static resources handling:

  • Serve Static Resources with Spring
  • Spring Framework 4.1 - handling static web resources
  • Spring MVC – How to include JS or CSS files in a JSP page

ADD 1

It seems option 2 provides much more flexibility regarding the resource mapping. And even resources within WEB-INF folder can be mapped.

like image 546
smwikipedia Avatar asked Dec 15 '15 01:12

smwikipedia


1 Answers

Here is a concrete example of when Falling Back On the "Default" Servlet To Serve Resources is not applicable.

This is a typical implementation of the above approach:

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

However, the current best practice for handling 404 errors in Spring 4 appears to be to use setThrowExceptionIfNoHandlerFound:

@Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext)
{
    DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
    dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    return dispatcherServlet;
}

Unfortunately, according to the documentation for DispatcherServlet:

Note that if DefaultServletHttpRequestHandler is used, then requests will always be forwarded to the default servlet and a NoHandlerFoundException would never be thrown in that case.

Indeed, this is the case. Combining both of the above approaches does not result in a NoHandlerFoundException being triggered, and this in turn prevents my 404 custom error page from resolving. Now, if I were to comment out my configureDefaultServletHandling method, the NoHandlerFoundException is thrown and my error handling (via @ControllerAdvice as shown in the linked answer) resolves to my custom 'notFoundPage'.

Unfortunately, this now means that my static resources (i.e., 'default.css') are not resolved:

DEBUG org.springframework.web.servlet.DispatcherServlet - Handler execution resulted in exception - forwarding to resolved error view: ModelAndView: reference to view with name 'notFoundPage'; model is {}
org.springframework.web.servlet.NoHandlerFoundException: No handler found for GET /webapp-test/style/default.css

I do not see any way to reconcile these two approaches so that they will not interfere with each other. My conclusion is that the "Default Servlet" approach is not appropriate for serving static resources in this case, which leaves us with the addResourceHandlers method.

Among the benefits of using the addResourceHandlers method are:

  • ...serve static resources from locations other than the web application root, including locations on the classpath.
  • The cache-period property may be used to set far future expiration headers so that they will be more efficiently utilized by the client.
  • The handler also properly evaluates the Last-Modified header (if present) so that a 304 status code will be returned as appropriate, avoiding unnecessary overhead for resources that are already cached by the client.

Also see this answer for a more complicated example of how handling static resources with the default servlet can cause unwanted side effects.

like image 181
vallismortis Avatar answered Nov 01 '22 06:11

vallismortis