ChatGPT解决这个技术问题 Extra ChatGPT

How to handle static content in Spring MVC?

I am developing a webapp using Spring MVC 3 and have the DispatcherServlet catching all requests to '/' like so (web.xml):

  <servlet>
    <servlet-name>app</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>app</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

Now this works as advertised, however how can I handle static content? Previously, before using RESTful URLs, I would have caught all *.html for example and sent that to the DispatcherServlet, but now it's a different ball game.

I have a /static/ folder which includes /styles/, /js/, /images/ etc and I would like to exclude /static/* from the DispatcherServlet.

Now I could get static resources working when I did this:

  <servlet-mapping>
    <servlet-name>app</servlet-name>
    <url-pattern>/app/</url-pattern>
  </servlet-mapping>

But I want it to have nice URLs (the point of me using Spring MVC 3) not the landing page being www.domain.com/app/

I also don't want a solution coupled to tomcat or any other servlet container, and because this is (relatively) low traffic I don't need a webserver (like apache httpd) infront.

Is there a clean solution to this?


N
Nimantha

Since I spent a lot of time on this issue, I thought I'd share my solution. Since spring 3.0.4, there is a configuration parameter that is called <mvc:resources/> (more about that on the reference documentation website) which can be used to serve static resources while still using the DispatchServlet on your site's root.

In order to use this, use a directory structure that looks like the following:

src/
 springmvc/
  web/
   MyController.java
WebContent/
  resources/
   img/
    image.jpg
  WEB-INF/
    jsp/
      index.jsp
    web.xml
    springmvc-servlet.xml

The contents of the files should look like:

src/springmvc/web/HelloWorldController.java:

package springmvc.web;

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

@Controller
public class HelloWorldController {
 
 @RequestMapping(value="/")
 public String index() {
  return "index";
 }
}

WebContent/WEB-INF/web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

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

 <servlet-mapping>
  <servlet-name>springmvc</servlet-name>
  <url-pattern>/</url-pattern>
 </servlet-mapping>
</web-app>

WebContent/WEB-INF/springmvc-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:context="http://www.springframework.org/schema/context"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/mvc
 http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- not strictly necessary for this example, but still useful, see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-controller for more information -->
 <context:component-scan base-package="springmvc.web" />

    <!-- the mvc resources tag does the magic -->
 <mvc:resources mapping="/resources/**" location="/resources/" />

    <!-- also add the following beans to get rid of some exceptions -->
 <bean      class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
 <bean
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
 </bean>

    <!-- JSTL resolver -->
 <bean id="viewResolver"
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="viewClass"
   value="org.springframework.web.servlet.view.JstlView" />
  <property name="prefix" value="/WEB-INF/jsp/" />
  <property name="suffix" value=".jsp" />
 </bean>

</beans>

WebContent/jsp/index.jsp:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h1>Page with image</h1>
<!-- use c:url to get the correct absolute path -->
<img src="<c:url value="/resources/img/image.jpg" />" />

This example should be in the Spring user guide - it's the best I've seen on this topic. Thanks Joris!
This definitely did the trick for me -- merely rozky's advice is what I started out doing but for some reason any page, other than my home-page, was rendering the resource-URL's relative to the page -- following this advice renders them relative to my app's context-root instead -- and works perfectly! Thanks!
thanks! I was pulling my hair out until I changed the line: to
As @Bane pointed out, the is a key part of this solution. Would you (or anyone) mind telling me why? Thanks!
You can also use and it will be mapped to the root. (i.e: The root will contain both resources AND the jsps). This can save you using c:url everywhere
r
rozky

This problem is solved in spring 3.0.4.RELEASE where you can use <mvc:resources mapping="..." location="..."/> configuration element in your spring dispatcher configuration file.

Check Spring Documentation


Though not actually "wrong", this answer is too brief because Spring's own documentation (which you referenced as your answer) appears to be missing something. Check Joris's answer for a more complete answer... not the fact that it's lengthy but the fact that he mentions the use of which neither your answer nor Spring's dox make mention of -- and which proved to be a critical part of the solution.
N
Nimantha

In Spring 3.0.x add the following to your servlet-config.xml (the file that is configured in web.xml as the contextConfigLocation. You need to add the mvc namespace as well but just Google for that if you don't know how!

That works for me

<mvc:default-servlet-handler/>

when I add this line I get: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 31 in XML document from class path resource [META-INF/spring/application-context.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 31; columnNumber: 35; cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'mvc:default-servlet-handler'.
Be sure to tackle with the order of the handler when you have some other view resolvers also.
u
user243397

If I understand your issue correctly, I think I have found a solution to your problem:

I had the same issue where raw output was shown with no css styles, javascripts or jquery files found.

I just added mappings to the "default" servlet. The following was added to the web.xml file:

 <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>

This should filter out the javascript and css file requests from the DispatcherRequest object.

Again, not sure if this is what you are after, but it worked for me. I think "default" is the name of the default servlet within JBoss. Not too sure what it is for other servers.


I actually don't want to use the default servlet- that couples me to jboss/tomcat
@hamo why is that a problem? (this is a genuine question, not a argumentative retort). You will need be running the server (jboss/tomcat/jetty) anyways for spring to run, right?
And you can add all <url-pattern> tags inside the same <servlet-mapping>
C
Community

There's another stack overflow post that has an excellent solution.

It doesn't seem to be Tomcat specific, is simple, and works great. I've tried a couple of the solutions in this post with spring mvc 3.1 but then had problems getting my dynamic content served.

In brief, it says add a servlet mapping like this:

<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/images/*</url-pattern>
</servlet-mapping>

h
hamo

I found a way around it using tuckey's urlrewritefilter. Please feel free to give a better answer if you have one!

In web.xml:

<filter>
    <filter-name>UrlRewriteFilter</filter-name>
    <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

  <servlet>
    <servlet-name>app</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>

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

In urlrewrite.xml:

<urlrewrite default-match-type="wildcard">
<rule>
    <from>/</from>
    <to>/app/</to>
</rule>
<rule match-type="regex">
    <from>^([^\.]+)$</from>
    <to>/app/$1</to>
</rule>
<outbound-rule>
    <from>/app/**</from>
    <to>/$1</to>
</outbound-rule>    

This means that any uri with a '.' in it (like style.css for example) won't be re-written.


The better answer is Spring 3's <mvc:resources/>, as demonstrated by @Joris.
n
nickdos

I've just been grappling with this issue in Spring MVC 3.0 and I initially went with the UrlRewriteFilter option. However I was not happy with this solution as it "didn't feel right" (I'm not the only one - see the link above to the Spring Forums where the word "hack" appears a few times).

So I came up with a similar solution to "Unknown (Google)" above but borrowed the idea of having all static content served from /static/ (taken from the Spring Roo version of the Pet Store app). The "default" servlet did not work for me but the Spring Webflow ResourceServlet did (also taken from Spring Roo generated app).

Web.xml:

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

<servlet>
    <servlet-name>Resource Servlet</servlet-name>
    <servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>mainDispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>Resource Servlet</servlet-name>
    <url-pattern>/static/*</url-pattern>
</servlet-mapping>

The only change I made to JSPs was to add the /static/ path to URLs for CSS, JS and images. E.g. "${pageContext.request.contextPath}/static/css/screen.css".

for Maven users the dependency for "org.springframework.js.resource.ResourceServlet" is:

<dependency>
    <groupId>org.springframework.webflow</groupId>
    <artifactId>org.springframework.js</artifactId>
    <version>2.0.8.RELEASE</version>
</dependency>

Not a bad solution nickdos- thank you! I still just don't "get it" as to why there isn't a resource servlet in core spring mvc (rather than having to add another dependency with web flow) or some other solution out of the box. Urlrewrite works fine for me so I'll stick with that for the time being! Cheers, Hamo
Looking back over the standard (non-Roo) version of the Spring Pet Clinic app, I noticed that the servlet definition for "default" is commented-out with the additional comment: "Uncomment this in containers (GlassFish) that do not declare this implicit definition out of the box". The explicit package declaration for default is org.apache.catalina.servlets.DefaultServlet. So this may be your "out of the box" resource servlet(?). I use Jetty for dev work and it seems Jetty does not provide an implicit default servlet (like Glassfish).
N
Nimantha

My own experience with this problem is as follows. Most Spring-related web pages and books seem to suggest that the most appropriate syntax is the following.

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

The above syntax suggests that you can place your static resources (CSS, JavaScript, images) in a folder named "resources" in the root of your application, i.e. /webapp/resources/.

However, in my experience (I am using Eclipse and the Tomcat plugin), the only approach that works is if you place your resources folder inside WEB_INF (or META-INF). So, the syntax I recommend is the following.

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

In your JSP (or similar) , reference the resource as follows.

<script type="text/javascript"
        src="resources/my-javascript.js">
</script>

Needless to mention, the entire question only arose because I wanted my Spring dispatcher servlet (front controller) to intercept everything, everything dynamic, that is. So I have the following in my web.xml.

<servlet>
    <servlet-name>front-controller</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>front-controller</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Finally, since I'm using current best practices, I have the following in my front controller servlet xml (see above).

<mvc:annotation-driven/>

And I have the following in my actual controller implementation, to ensure that I have a default method to handle all incoming requests.

@RequestMapping("/")

That's was the only solution that worked for me. One interesting thing I noticed is that new resources added after I started the webapp weren't found until a restart. Doesn't make sense from the user perspective, but should not be a major problem most of the time.
You told with Tomcat perspective, awesome :)
I tried each answer on this page. Thankfully when i got to this one i din't have to try any more.
N
Nimantha

I got the same problem and found Joris's answer very helpful. But additionally I need to add

<mvc:annotation-driven /> 

to the servlet config file. Without that resource mapping will not work and all handlers will stop working.


B
BreakerOfStones

The URLRewrite is sort of a "hack" if you want to call it that. What it comes down to is, you're re-inventing the wheel; as there are already existing solutions. Another thing to remember is Http Server = Static content & App server = dynamic content (this is how they were designed). By delegating the appropriate responsibilities to each server you maximize efficiency... but now-a-days this is probably only a concern in a performance critical environments and something like Tomcat would most likely work well in both roles most of the time; but it is still something to keep in mind none the less.


C
Chepech

I solved it this way:

<servlet-mapping>
    <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.gif</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>*.css</url-pattern>
</servlet-mapping>

This works on Tomcat and ofcourse Jboss. However in the end I decided to use the solution Spring provides (as mentioned by rozky) which is far more portable.


R
Ruju

I used both ways that is urlrewrite and annotation based in spring mvc 3.0.x and found that annotation based approach is most suitable that is

<annotation-driven />

<resources mapping="/resources/**" location="/resources/" />

In case of urlrewrite,have to define lots of rule and some time also get class not found exception for UrlRewriteFilter as already provided the dependency for it. I found that it's happening due to the presence of transitive dependency, so again one step will increased and have to exclude that dependency from pom.xml using

<exclusion></exclusion> tags.

So annotation based approach will be the good deal.


B
Balasubramanian Jayaraman

From Spring 3, all the resources needs to mapped in a different way. You need to use the tag to specify the location of the resources.

Example :

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

By doing this way, you are directing the dispatcher servlet to look into the directory resources to look for the static content.


T
Teja Kantamneni

My way of solving this problem is placing all your actions with a specific prefix like "web" or "service" and configure that all url's with that prefix will be intercepted by the DispatcherServlet.


P
Pablo Cantero

I just add three rules before spring default rule (/**) to tuckey's urlrewritefilter (urlrewrite.xml) to solve the problem

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN" "http://tuckey.org/res/dtds/urlrewrite3.0.dtd">
    <urlrewrite default-match-type="wildcard">
     <rule>
      <from>/</from>
      <to>/app/welcome</to>
     </rule>
     <rule>
      <from>/scripts/**</from>
      <to>/scripts/$1</to>
     </rule>
     <rule>
      <from>/styles/**</from>
      <to>/styles/$1</to>
     </rule>
     <rule>
      <from>/images/**</from>
      <to>/images/$1</to>
     </rule>
     <rule>
      <from>/**</from>
      <to>/app/$1</to>
     </rule>
     <outbound-rule>
      <from>/app/**</from>
      <to>/$1</to>
     </outbound-rule> 
    </urlrewrite>

0
0DayHack

I know there are a few configurations to use the static contents, but my solution is that I just create a bulk web-application folder within your tomcat. This "bulk webapp" is only serving all the static-contents without serving apps. This is pain-free and easy solution for serving static contents to your actual spring webapp.

For example, I'm using two webapp folders on my tomcat.

springapp: it is running only spring web application without static-contents like imgs, js, or css. (dedicated for spring apps.) resources: it is serving only the static contents without JSP, servlet, or any sort of java web application. (dedicated for static-contents)

If I want to use javascript, I simply add the URI for my javascript file.

EX> /resources/path/to/js/myjavascript.js

For static images, I'm using the same method.

EX> /resources/path/to/img/myimg.jpg

Last, I put "security-constraint" on my tomcat to block the access to actual directory. I put "nobody" user-roll to the constraint so that the page generates "403 forbidden error" when people tried to access the static-contents path.

So far it works very well for me. I also noticed that many popular websites like Amazon, Twitter, and Facebook they are using different URI for serving static-contents. To find out this, just right click on any static content and check their URI.


J
Jorge Sanchez

This did the real job in my case

in web.xml:

...
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/images/*</url-pattern>
    <url-pattern>/css/*</url-pattern>
    <url-pattern>/javascripts/*</url-pattern>
</servlet-mapping>


<servlet-mapping>
    <servlet-name>spring-mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

...


V
Vipul Panth

For java based spring configuration you can use the following

Using ResourceHandlerRegistry which stores registrations of resource handlers for serving static resources.

More Info @ WebMvcConfigurerAdapter which defines callback methods to customize the Java-based configuration for Spring MVC enabled via @EnableWebMvc.

@EnableWebMvc
@Configurable
@ComponentScan("package.to.scan")
public class WebConfigurer extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static_resource_path/*.jpg").addResourceLocations("server_destination_path");

    }

n
ngeek

After encountering and going through the same decision making process described here, I decided to go with the ResourceServlet proposal which works out quite nicely.

Note that you get more information on how to use webflow in your maven build process here: http://static.springsource.org/spring-webflow/docs/2.0.x/reference/html/ch01s05.html

If you use the standard Maven central repository the artifact is (in opposite to the above referred springsource bundle):

<dependency>
    <groupId>org.springframework.webflow</groupId>
    <artifactId>spring-js</artifactId>
    <version>2.0.9.RELEASE</version>
</dependency> 

C
Community

This can be achieved in at least three ways.

Solutions:

expose the html as a resource file

instruct the JspServlet to also handle *.html requests

write your own servlet (or pass to another existing servlet requests to *.html).

For complete code examples how to achieve this please reffer to my answer in another post: How to map requests to HTML file in Spring MVC?


A
Asif Malek

The Problem is with URLPattern

Change your URL pattern on your servlet mapping from "/" to "/*"


J
Jekin Kalariya
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<mvc:default-servlet-handler/>
</beans>

and if you want to use annotation based configuration use below code

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

b
barbsan

Place static contents like css ,js in following path

resources 
        ->static
               ->css
               ->js
(or) 
resources 
        ->public
               ->css
               ->js

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now