Can SpringMVC be configured to process all requests, but exclude static content directories?

SpringConfigurationSpring Mvc

Spring Problem Overview


If I map my spring application to process all incoming requests ('/*'), then requests for static content return 404's. For example, a request for "myhost.com/css/global.css" would return a 404, even though the resource exists as Spring intercepts the request.

The alternative is to map SpringMVC to a subdirectory (for example '/home/'), but in this case, you must pass this directory in all links within the application. Is there a way to map SpringMVC to '/' and exclude a set of directories from processing?

My current web.xml configuration is:

<servlet>
	<servlet-name>springApp</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>springApp</servlet-name>
	<url-pattern>/home/*</url-pattern>
</servlet-mapping>

Idealy I would like to have the mapping be something like the following:

 <servlet-mapping>
    <servlet-name>springApp</servlet-name>
    <url-pattern>/*</url-pattern>
    <exclude>/css/*,/js/*</exclude>
 </servlet-mapping>

Is this type of thing possible?

Spring Solutions


Solution 1 - Spring

NOTE: this answer applies to Spring 3.0.4+ ONLY

(BTW, this question has also been dealt with here: https://stackoverflow.com/questions/3829369/spring-serving-static-content-with-mvcresources-invalid-xsd)

Check out the Spring mvc-showcase project in the Spring subversion samples repository. It shows exactly what you want to do, namely that you can delineate static resources which will not be processed by the DisapatcherServlet. See file /mvc-showcase/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml. Here's a snippet of how I handle these exclusions, where the JS, CSS, and images are in the app context root (with the MVC namespace mapped to mvc:

<!-- resources exclusions from servlet mapping -->
<mvc:resources mapping="/css/**" location="/css/" />
<mvc:resources mapping="/images/**" location="/images/" />
<mvc:resources mapping="/js/**" location="/js/" />

Solution 2 - Spring

I solved by serving static content through the 'default' servlet, that just serve the content to the client. So my web.xml looks like this:

<servlet>
    <servlet-name>MyApp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>MyApp</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping> <!-- The 'dynamic' content -->

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping> <!-- The 'static' content -->

Hope this helps.

Solution 3 - Spring

If you want to do this with Spring only, it's possible but a bit messy:

  1. You'll either need to use a http://static.springsource.org/spring/docs/2.5.x/reference/mvc.html#mvc-handlermapping">SimpleUrlHandlerMapping</a> for which you can explicitly specify URL patterns which should be mapped to controllers OR extend it to support "ignore" URLs like "css/**".
  2. You'll need to write your own http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/web/HttpRequestHandler.html">HttpRequestHandler</a> implementation that would basically consist of "getServletContext().getRequestDsipatcher().include()" call to return the requested resource as is.
  3. You'll have to register that handler as defaultHandler for the above SimpleUrlHandlerMapping.

Once all that is done, all requests that can't be mapped to your controllers will be forwarded to your HttpRequestHandler and served "as is".

Solution 4 - Spring

Simplest way for me (if using a late enough version of Spring) is

<mvc:resources mapping="/**/*.js" location="/"/>
<mvc:resources mapping="/**/*.css" location="/"/>
...

Solution 5 - Spring

One way to do it would be with Filters. You'd have to write a little bit of custom code but it's not bad. Here's an example if you don't want to pass *.css or *.js files to your Spring servlet:

web.xml:

<filter-mapping>
    <filter-name>fileTypeFilter</filter-name>
    <filter-class>foo.FileTypeFilter</filter-class>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Java class:

public class FileTypeFilter implements Filter {
     public void init(FilterConfig conf) {
         // init logic here
     }

     public void destroy() {
        // release resources here
     }

     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
          if(shouldExclude(req)) {
              chain.doFilter(req, res);
              //some logic so the request doesnt go to the servlet

              //maybe you could just forward
              //the request directly to the file getting accessed.  not sure if that would work
          }

          //file should be passed to the servlet; you can do some logic here
          //if you want         
     }
     private boolean shouldExclude(ServletRequest req) {
         if(req instanceof HttpServletRequest) {
             HttpServletRequest hreq = (HttpServletRequest) req;
             return (hreq.getRequestURI().endsWith(".css") ||
                     hreq.getRequestURI().endsWith(".js"));
         }
         return false;
    }
}

I haven't tested this, but I think it will work.

EDIT: There's isn't any exclude functionality in the servlet spec. I don't think there is a good way to do this within Spring, but it essentially achieves the same thing in your post.

EDIT 2: If you want to be able to easily change what gets filtered, you could just use Spring to inject something into the Filter at runtime.

EDIT 3: I just realized if you forward directly to the file, it'll do the filter again and you'll get caught in an infinite loop. There might be another way to do this with filters, but I'm honestly not sure what it is.

Solution 6 - Spring

What are you using to serve your static images? If it's Apache then you could configure Apache to not pass css/js requests to your app server.

If you are using Tomcat you'd put something like this in your httpd.conf:

JkUnMount /*.css  webapp

Where 'webapp' is the entry from your workers.properties.

Sorry I can't give you a pure Spring solution, but this is how I do it.

Solution 7 - Spring

I got the same problem and here is how I solved it:

The following was added to the web.xml file:

<servlet-mapping>
	<servlet-name>default</servlet-name>
	<url-pattern>*.js</url-pattern>
	<url-pattern>*.css</url-pattern>
	<url-pattern>*.ico</url-pattern>
	<url-pattern>*.png</url-pattern>
	<url-pattern>*.jpg</url-pattern>
	<url-pattern>*.htc</url-pattern>
	<url-pattern>*.gif</url-pattern>
	<url-pattern>*.html</url-pattern>
	<url-pattern>*.htm</url-pattern>
</servlet-mapping>

The following was added to the spring3 MVC servlet bean definition file (such as applicationContext.xml, the file that is configured in web.xml as the contextConfigLocation.):

<mvc:annotation-driven /> 
<mvc:default-servlet-handler />

Solution 8 - Spring

Do you have a consistent extension(s) for the requests you want processed by the Spring dispatcher (I believe most of the Spring examples use a *.htm)? In that case, you could map to the extensions you wish to have processed which would bypass your css and js files.

Otherwise I'd agree with Nalandial, the Filter approach is probably the best work around at this point.

Solution 9 - Spring

I use virtual URL path to retrieve the resource I need. Typically I use Spring MVC, so I couldn't have javascripts and css under /WEB-INF/views folder. I came up with this custom servlet to ONLY allow access to .js & .css files within /WEB-INF/views folder. In your case, if you move the /css folder and /js folder to a parent folder such as /resource then my solution will be applicable to you.

You can change the String url = "YOUR_RESOURCE_FOLDER"

So for example, virtual path can be something like http://www.mysite.com/resources/path/path/app.js

That will map to my /WEB-INF/views/path/path/app.js

web.xml

<servlet>
    <servlet-name>ResourceDispatcherServlet</servlet-name>
    <servlet-class>mywebapp.web.ResourceDispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>ResourceDispatcherServlet</servlet-name>
    <url-pattern>/resource/*</url-pattern>
</servlet-mapping>

servlet

public class ResourceDispatcherServlet extends HttpServlet {

    public void init() throws ServletException {

    }

    public void doGet(HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException {


        String servletPath = req.getServletPath();   // /resource
        String pathInfo = req.getPathInfo();         // /path/path/app.js

        String url = "/WEB-INF/views" + pathInfo;

        String lastPath = StringUtil.substringAfterLast(pathInfo, "/");
        String extension = StringUtil.substringAfterLast(lastPath, ".");

        try {
            RequestDispatcher dispatcher = null;
            if (!StringUtil.isEmpty(extension) && ("js".equals(extension) || "css".equals(extension))) {
                dispatcher = req.getRequestDispatcher(url);
            }

            if (dispatcher != null) {
                dispatcher.include(req, rsp);
            }
            else {
                rsp.sendError(404);
            }
        }
        catch (Exception e) {
            if (!rsp.isCommitted()) {
                rsp.sendError(500);
            }
        }
    }
}

Solution 10 - Spring

If you are using Spring 3.0.4 and above you should use solution provided by atrain

Otherwise, you can do this simple thing:

perhaps you have following static directory structure you want to serve:

WebContent
   |
   WEB-INF
     |
    public
        |
       css
        |
       js
        |
       img
 

Eclipse Dynamic web projects by default generate following structure: WebContent/WEB-INF. Move the public folder out of your WEB-INF directory into WebContentdirectory.

On client side

refer your static files in following way:

<link rel="stylesheet" type="text/css" href="public/css/mystyles.css">

Here is my reference.

Solution 11 - Spring

In my case everything was ok. But i have a problem in a Controller

that was my problem @RequestMapping( method = RequestMethod.GET)

y change for this:

@RequestMapping(value = "/usuario", method = RequestMethod.GET)

and it works

look for a controller that has bad @RequestMappgin and change.

Solution 12 - Spring

It's cleaner to use UrlRewriteFilter to redirect the request to your servlet, here an example of urlrewrite.xml

<urlrewrite>
    <rule>
        <from>^/img/(.*)$</from>
        <to>/img/$1</to>
    </rule>
    <rule>
        <from>^/js/(.*)$</from>
        <to>/js/$1</to>
    </rule>
    <rule>
        <from>^/css/(.*)$</from>
        <to>/css/$1</to>
    </rule>
    <rule>
        <from>^/(.*)$</from>
        <to>/app/$1</to>
    </rule>
    <outbound-rule>
        <from>/app/(.*)$</from>
        <to>/$1</to>
    </outbound-rule>
</urlrewrite>

NOTES:

  • It's important the last <rule> is in the bottom so img, js, css will be caught first
  • The <outbound-rule> is optional and is just to make the existing
    <c:url value="/app/some" /> render /some instead of /app/some

Solution 13 - Spring

Usually, big websites prefer using another server only to handle static content. Requests of static content goes to one server and dynamic goes to another (with spring, in this case).

In many cases, Nginx server (http://nginx.com/), a recent and very fast server.

But this is not trivial to do. A lot of configurations.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRich KrollView Question on Stackoverflow
Solution 1 - SpringatrainView Answer on Stackoverflow
Solution 2 - Springuser153415View Answer on Stackoverflow
Solution 3 - SpringChssPly76View Answer on Stackoverflow
Solution 4 - SpringDaniel EganView Answer on Stackoverflow
Solution 5 - SpringAlex BeardsleyView Answer on Stackoverflow
Solution 6 - SpringDarren GreavesView Answer on Stackoverflow
Solution 7 - SpringGreen LeiView Answer on Stackoverflow
Solution 8 - SpringjridleyView Answer on Stackoverflow
Solution 9 - Springwebpro2828View Answer on Stackoverflow
Solution 10 - SpringVikramView Answer on Stackoverflow
Solution 11 - Springuser3193801View Answer on Stackoverflow
Solution 12 - Springvictor hugoView Answer on Stackoverflow
Solution 13 - SpringVitor BragaView Answer on Stackoverflow