如何在Spring MVC中处理静态内容?


200

我正在使用Spring MVC 3开发一个webapp,并DispatcherServlet像这样(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>

现在,它可以像宣传的那样工作,但是如何处理静态内容?以前,在使用RESTful URL之前,我会捕获所有* .html并将其发送到DispatcherServlet,但是现在这是一个不同的球类游戏。

我有一个/ static /文件夹,其中包括/ styles /,/ js /,/ images /等,我想从中排除/ static / * DispatcherServlet

现在,当执行此操作时,我可以使用静态资源:

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

但是我希望它有一个不错的URL(我使用Spring MVC 3的要点),而不是登录页面是www.domain.com/app/

我也不需要耦合到tomcat或任何其他servlet容器的解决方案,而且由于(相对)低流量,我不需要前端的Web服务器(如apache httpd)。

有一个干净的解决方案吗?



Answers:


266

由于我在这个问题上花了很多时间,所以我想分享我的解决方案。从spring 3.0.4开始,有一个配置参数被调用<mvc:resources/>(有关更多信息,请参阅参考文档网站),该参数可用于提供静态资源,同时仍在站点根目录上使用DispatchServlet。

为了使用此功能,请使用如下所示的目录结构:

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

文件内容应如下所示:

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

希望这可以帮助 :-)


12
这个例子应该在Spring用户指南中-这是我在该主题上看到的最好的例子。谢谢乔里斯!
保罗

这绝对是我的诀窍-刚开始我只是做rozky的建议,但由于某种原因,除了我的主页之外,任何页面都在呈现资源URL与页面的相对关系-根据此建议,它们呈现为相对改为我应用的上下文根目录-完美运行!谢谢!
班恩(Bane)2012年

谢谢!我一直在梳理头发,直到更改以下行:<mvc:resources mapping =“ / resources / **” location =“ /,classpath:/ META-INF / web-resources /” />到<mvc:resources mapping =“ / resources / **” location =“ / resources /” />
Mark D

2
正如@Bane指出的那样,<c:url value = ... />是此解决方案的关键部分。您(或任何人)可以告诉我为什么吗?谢谢!
标记

4
您也可以使用<mvc:resources mapping =“ / **” location =“ / resources /” />,它将被映射到根目录。(即:根将同时包含资源和jsps)。这可以节省您在任何地方使用c:url的时间
efaj 2013年

46

在spring 3.0.4.RELEASE中解决了这个问题,您可以<mvc:resources mapping="..." location="..."/> 在spring调度程序配置文件中使用 配置元素。

检查Spring文档


5
尽管实际上并非“错误”,但该答案太简短了,因为Spring自己的文档(您将其称为答案)似乎缺少了一些东西。检查Joris的答案以获得更完整的答案...不是冗长的事实,而是他提到使用<c:url ...>的事实,您的答案和Spring的dox都没有提及-并且证明了成为解决方案的关键部分。
班恩(Bane)2012年

38

在Spring 3.0.x中,将以下内容添加到servlet-config.xml(在web.xml中配置为contextConfigLocation的文件。您还需要添加mvc命名空间,但如果不知道如何,只需添加google即可!;)

这对我行得通

<mvc:default-servlet-handler/>

问候

阿尤布·马利克(Ayub Malik)


当我添加此行时,我得到:org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:类路径资源[META-INF / spring / application-context.xml]中XML文档中的第31行无效;嵌套的异常是org.xml.sax.SAXParseException; lineNumber:31;columnNumber:35;cvc-complex-type.2.4.c:匹配的通配符很严格,但是找不到元素'mvc:default-servlet-handler'的声明。
亚历克斯·沃登

当您还有其他视图解析器时,请确保按处理程序的顺序处理。
凤凰城

20

如果我正确理解了您的问题,则认为我已经找到了解决您问题的方法:

我有一个相同的问题,其中显示原始输出,没有找到CSS样式,JavaScript或jquery文件。

我只是将映射添加到“默认” servlet。以下内容已添加到web.xml文件中:

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

这应该过滤掉DispatcherRequest对象中的javascript和css文件请求。

同样,不确定这是否是您要使用的,但是对我有用。我认为“默认”是JBoss中默认servlet的名称。不太清楚其他服务器的用途。


1
我实际上不想使用默认的servlet-将我耦合到jboss / tomcat
hamo 2010年

@hamo为什么会有问题?(这是一个真实的问题,不是争论性的反驳)。无论如何,您都将需要运行服务器(jboss / tomcat / jetty)才能运行spring,对吗?
Manav

3
您可以将所有<url-pattern>标签添加在同一标签内<servlet-mapping>
Milanka 2013年

16

还有另一个堆栈溢出帖子,它有一个很好的解决方案

它似乎不是特定于Tomcat的,它很简单,并且效果很好。我已经在Spring MVC 3.1中尝试了本文中的一些解决方案,但是在获取动态内容时遇到了问题。

简而言之,它说添加一个像这样的servlet映射:

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

11

我使用tuckey的urlrewritefilter找到了解决方法。如果有,请随时给出更好的答案!

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

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

这意味着任何带有'。'的uri。其中的内容(例如style.css)不会被重写。


5
更好的答案是Spring 3 <mvc:resources/>,如@Joris所示。
保罗

11

我刚刚在Spring MVC 3.0中解决了这个问题,最初使用了UrlRewriteFilter选项。但是,我对此解决方案感到不满意,因为它“感觉不对”(我不是唯一的人-请参见上面指向Spring论坛的链接,其中“ hack”一词出现了几次)。

因此,我想出了与上述“未知(Google)”类似的解决方案,但借用了从/ static /(从Pet Store应用程序的Spring Roo版本中获取)提供所有静态内容的想法。“默认” servlet对我不起作用,但是Spring Webflow ResourceServlet起作用(也取自Spring Roo生成的应用程序)。

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>

我对JSP所做的唯一更改是将/ static /路径添加到CSS,JS和图像的URL。例如“ $ {pageContext.request.contextPath} /static/css/screen.css”。

对于Maven用户,“ org.springframework.js.resource.ResourceServlet”的依赖性为:

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

nickdos不错的解决方案-谢谢!我仍然只是不“明白”为什么在核心spring mvc中没有资源servlet(而不是必须通过Web流添加另一个依赖项)或其他一些开箱即用的解决方案。Urlrewrite对我来说效果很好,所以我暂时坚持使用!干杯欢呼
声-hamo

2
回顾Spring Pet Clinic应用程序的标准(非Roo)版本,我注意到“默认”的servlet定义已注释掉,并带有附加注释:“在未声明此内容的容器(GlassFish)中取消注释此注释隐式定义”。默认的显式包声明是org.apache.catalina.servlets.DefaultServlet。因此,这可能是您的“开箱即用”资源servlet(?)。我使用Jetty进行开发工作,似乎Jetty没有提供隐式默认servlet(例如Glassfish)。
nickdos

8

我对这个问题的经验如下。大多数与Spring相关的网页和书籍似乎都建议最合适的语法如下。

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

上面的语法建议您可以将静态资源(CSS,JavaScript,图像)放在应用程序根目录(即/ webapp / resources /)中名为“ resources”的文件夹中。

但是,以我的经验(我正在使用Eclipse和Tomcat插件),唯一有效的方法是将资源文件夹放在 WEB_INF(或META-INF)中。因此,我推荐的语法如下。

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

在您的JSP(或类似版本)中,如下引用资源。

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

不用说,整个问题只是因为我希望我的Spring调度程序servlet(前端控制器)拦截所有动态的东西而已。因此,我的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>

最后,由于我正在使用当前的最佳实践,因此在前端控制器servlet xml中有以下内容(请参见上文)。

<mvc:annotation-driven/>

在实际的控制器实现中,我有以下内容,以确保我有一个默认方法来处理所有传入的请求。

@RequestMapping("/")

我希望这有帮助。


那是唯一对我有用的解决方案。我注意到的一件有趣的事情是,在启动Webapp之后添加的新资源直到重启后才被发现。从用户角度看,这没有任何意义,但在大多数情况下都不应该成为主要问题。
拉斐尔·斯蒂尔

您以Tomcat的角度告诉我们,太棒了:)
omkar sirra

我尝试了此页面上的每个答案。值得庆幸的是,当我到达这一步时,我不必再尝试了。
TimeTrax

6

我遇到了同样的问题,发现Joris的回答很有帮助。但是我还需要添加

<mvc:annotation-driven /> 

到servlet配置文件。没有该资源映射将无法工作,所有处理程序将停止工作。希望这会帮助某人。


2

如果要调用URLRewrite,它就是一种“ hack”。归结为,您正在重新发明轮子。因为已经有现有的解决方案。要记住的另一件事是Http Server =静态内容&App server =动态内容(这是它们的设计方式)。通过将适当的职责委派给每台服务器,可以最大程度地提高效率……但是,如今,这可能只是性能关键环境中的一个问题,而像Tomcat这样的东西在大多数情况下都很可能在两种角色中都能正常工作;但这仍然是要牢记的。


2

我是这样解决的:

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

这适用于Tomcat,当然也适用于Jboss。但是最终,我决定使用Spring提供的解决方案(如rozky所提到的),该解决方案具有更高的可移植性。


2

我在spring mvc 3.0.x中使用了基于urlrewrite和注释的两种方法,发现基于注释的方法最适合

<annotation-driven />

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

在urlrewrite的情况下,必须定义很多规则,并且有些时候还为UrlRewriteFilter获取未找到的类异常,因为已经为其提供了依赖性。我发现这是由于存在传递依赖项而发生的,因此再次增加了一步,必须使用以下命令将其从pom.xml中排除

<exclusion></exclusion> tags.

因此,基于注释的方法将是不错的选择。


2

从Spring 3开始,所有资源都需要以不同的方式进行映射。您需要使用标记来指定资源的位置。

范例:

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

通过这种方式,您将指导调度程序Servlet进入目录资源以寻找静态内容。


1

解决此问题的方法是,将所有操作放置在具有特定前缀(例如“ web”或“ service”)的位置,并配置所有具有该前缀的url将被DispatcherServlet拦截。


1

我只是在春季默认规则(/ **)之前添加了三个规则到Tuckey的urlrewritefilter(urlrewrite.xml)中以解决问题

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

1

我知道有一些配置可以使用静态内容,但是我的解决方案是仅在雄猫中创建一个批量Web应用程序文件夹。此“批量网络应用”仅提供所有静态内容,不提供应用。这是为您的实际spring webapp提供静态内容的轻松,轻松的解决方案。

例如,我在雄猫上使用了两个webapp文件夹。

  1. springapp:它只运行没有静态内容(例如imgs,js或css)的spring Web应用程序。(专用于春季应用。)
  2. resources:仅提供静态内容,而没有JSP,Servlet或任何类型的Java Web应用程序。(专用于静态内容)

如果要使用javascript,只需为我的javascript文件添加URI。

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

对于静态图像,我使用相同的方法。

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

最后,我把“ 安全约束在tomcat上 ”以阻止对实际目录的访问。我将“ nobody”用户权限设置为约束,以便当人们尝试访问静态内容路径时页面生成“ 403禁止的错误”。

到目前为止,它对我来说效果很好。我还注意到,许多流行的网站(如Amazon,Twitter和Facebook)都使用不同的URI来提供静态内容。要找出这一点,只需右键单击任何静态内容并检查其URI。


1

就我而言,这才是真正的工作

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

...


1

对于基于Java的Spring配置,可以使用以下命令

使用ResourceHandlerRegistry来存储用于处理静态资源的资源处理程序的注册。

更多信息@ WebMvcConfigurerAdapter定义了回调方法,以通过@EnableWebMvc为Spring MVC定制基于Java的配置。

@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");

    }

0

遇到并经历了此处描述的相同决策过程后,我决定采用ResourceServlet提案,该提案效果很好。

请注意,您在此处获得有关如何在Maven构建过程中使用Webflow的更多信息:http : //static.springsource.org/spring-webflow/docs/2.0.x/reference/html/ch01s05.html

如果您使用标准的Maven中央存储库,则工件是(与上面提到的springsource包相反):

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

0

这可以通过至少三种方式来实现。

解决方案

  • 将html公开为资源文件
  • 指示JspServlet也处理* .html请求
  • 编写您自己的servlet(或传递给* .html的另一个现有servlet请求)。

有关如何实现此目标的完整代码示例,请参考我在另一篇文章中的回答:如何在Spring MVC中将请求映射到HTML文件?



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

如果要使用基于注释的配置,请使用以下代码

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

0

将静态内容(如css,js)放在以下路径中

resources 
        ->static
               ->css
               ->js
(or) 
resources 
        ->public
               ->css
               ->js
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.