SpringMVC 是不是可以配置为处理所有请求,但排除静态内容目录?

Posted

技术标签:

【中文标题】SpringMVC 是不是可以配置为处理所有请求,但排除静态内容目录?【英文标题】:Can SpringMVC be configured to process all requests, but exclude static content directories?SpringMVC 是否可以配置为处理所有请求,但排除静态内容目录? 【发布时间】:2010-11-17 02:02:08 【问题描述】:

如果我将 Spring 应用程序映射为处理所有传入请求 ('/*'),则静态内容请求会返回 404。例如,对“myhost.com/css/global.css”的请求将返回 404,即使在 Spring 拦截请求时资源存在。

替代方法是将 SpringMVC 映射到子目录(例如 '/home/'),但在这种情况下,您必须在应用程序的所有链接中传递此目录。有没有办法将 SpringMVC 映射到“/”并从处理中排除一组目录?

我当前的 web.xml 配置是:

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

理想情况下,我希望映射如下所示:

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

这种事情可能吗?

【问题讨论】:

【参考方案1】:

您使用什么来提供静态图片? 如果是 Apache,那么您可以将 Apache 配置为不将 css/js 请求传递给您的应用服务器。

如果你使用的是 Tomcat,你会在你的 httpd.conf 中加入这样的内容:

JkUnMount /*.css  webapp

“webapp”是您的workers.properties 中的条目。

对不起,我不能给你一个纯 Spring 的解决方案,但我就是这样做的。

【讨论】:

这是一个很好的提示,但我正在寻找便携的东西;因此,当组装好的 WAR 放入 servlet 容器中时,无需外部配置即可工作。【参考方案2】:

一种方法是使用过滤器。您必须编写一些自定义代码,但这还不错。如果您不想将 *.css 或 *.js 文件传递​​给 Spring servlet,这是一个示例:

web.xml:

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

Java 类:

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;
    

我没有对此进行测试,但我认为它会起作用。

编辑:servlet 规范中没有任何排除功能。我认为在 Spring 中没有很好的方法来做到这一点,但它在你的帖子中基本上实现了同样的事情。

编辑 2:如果您希望能够轻松更改过滤的内容,您可以使用 Spring 在运行时向过滤器中注入一些内容。

编辑 3:我刚刚意识到,如果您直接转发到文件,它会再次进行过滤,您将陷入无限循环。使用过滤器可能还有另一种方法,但老实说我不确定它是什么。

【讨论】:

【参考方案3】:

对于希望由 Spring 调度程序处理的请求,您是否有一致的扩展(我相信大多数 Spring 示例都使用 *.htm)?在这种情况下,您可以映射到您希望处理的扩展,从而绕过您的 css 和 js 文件。

否则我会同意 Nalandial,过滤器方法可能是目前最好的解决方法。

【讨论】:

我使用的是restful url,所以应用程序中没有任何扩展名,所以后缀映射对我不起作用。【参考方案4】:

如果你只想用 Spring 做这件事,有可能但有点混乱:

    您要么需要使用SimpleUrlHandlerMapping,您可以明确指定应该映射到控制器的 URL 模式,或者扩展它以支持“忽略”URL,如“css/**”。 您需要编写自己的 HttpRequestHandler 实现,该实现基本上由“getServletContext().getRequestDsipatcher().include()”调用组成,以按原样返回请求的资源。 您必须将该处理程序注册为上述 SimpleUrlHandlerMapping 的 defaultHandler。

完成所有操作后,所有无法映射到您的控制器的请求都将转发到您的 HttpRequestHandler 并“按原样”提供服务。

【讨论】:

我目前正在使用 DefaultAnnotationHandlerMapping 进行基于注释的控制器映射。根据您的建议,我将对其进行扩展并添加对基于路径的排除的支持。感谢您为我指明正确的方向。 如果有人错过了它,请注意 Spring 3.x 存在更好的解决方案。看看下面的答案!【参考方案5】:

我通过“默认”servlet 提供静态内容来解决问题,该 servlet 只是将内容提供给客户端。所以我的 web.xml 看起来像这样:

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

希望这会有所帮助。

【讨论】:

我也遇到了这个解决方案——我唯一的问题是:我不相信它是可移植的,b:我无法通过注释映射“/”路径。如果能解决这两个问题,这对我来说将是完美的解决方案。 对不起,如果我迟到了,我不在城里。是的,关于可移植性问题,您可能是对的。我没有这个问题,因为我被困在tomcat上。可能你可以在战争中包含 DefaultServlet。关于通过注释的“/”路径,在我的配置(方法级注释的IndexController)中,它与@RequestMapping(value =“/”)一起使用。使用类级注释控制器,有时我需要以这种方式注释 @RequestMapping(value = "") 如果您正在运行开发人员实例并且不想为为静态媒体设置 Web 服务器而烦恼。 对于它的价值,我已经尝试过了,这适用于 Tomcat 7 和 Jetty 6。 这似乎不适用于 Glassfish 3.1.2.2(构建 5)。我在部署期间收到以下错误:“这里没有默认名称的 Web 组件。”有什么想法吗?【参考方案6】:

使用UrlRewriteFilter 将请求重定向到您的servlet 更简洁,这里是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>

注意事项:

重要的是最后一个&lt;rule&gt; 在底部,所以img、js、css 将首先被捕获 &lt;outbound-rule&gt; 是可选的,只是为了使现有的&lt;c:url value="/app/some" /&gt; 呈现 /some 而不是 /app/some

【讨论】:

【参考方案7】:

注意:此答案仅适用于 Spring 3.0.4+

(顺便说一句,这里也处理过这个问题:Spring serving static content with mvc:resources, invalid xsd)

查看 Spring subversion samples repository 中的 Spring mvc-showcase 项目。它准确地显示了您想要做什么,即您可以描述 DisapatcherServlet 不会处理的静态资源。见文件/mvc-showcase/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml。这是我如何处理这些排除的 sn-p,其中 JS、CSS 和图像位于应用程序上下文根中(MVC 命名空间映射到 mvc

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

【讨论】:

注意:Spring MVC Showcase 项目已移至 github。在此处访问:github.com/SpringSource/spring-mvc-showcase 你如何在配置中使用注解做同样的事情? nm ***.com/questions/14299149/…【参考方案8】:

通常,大型网站更喜欢仅使用另一台服务器来处理静态内容。 静态内容的请求发送到一台服务器,动态发送到另一台服务器(在这种情况下使用 spring)。

在很多情况下,nginx 服务器 (http://nginx.com/) 是一个最新且非常快的服务器。

但这并非易事。很多配置。

【讨论】:

【参考方案9】:

对我来说最简单的方法(如果使用足够晚的 Spring 版本)是

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

【讨论】:

我喜欢这个解决方案,因为我将 spring 添加到一个非常古老的站点,该站点到处都是静态内容。我遇到的所有其他示例都没有考虑到这一点。谢谢!【参考方案10】:

我使用虚拟 URL 路径来检索我需要的资源。通常我使用 Spring MVC,所以 /WEB-INF/views 文件夹下不能有 javascripts 和 css。我想出了这个自定义 servlet,只允许访问 /WEB-INF/views 文件夹中的 .js 和 .css 文件。在您的情况下,如果您将 /css 文件夹和 /js 文件夹移动到 /resource 等父文件夹,那么我的解决方案将适用于您。

您可以更改字符串 url = "YOUR_RESOURCE_FOLDER"

例如,虚拟路径可以是 http://www.mysite.com/resources/path/path/app.js

这将映射到我的 /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>

小服务程序

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

【讨论】:

【参考方案11】:

如果您使用的是 Spring 3.0.4 及更高版本,则应使用solution provided by atrain

否则,您可以这样做简单

也许您有以下要服务的静态目录结构:

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

Eclipse 动态 Web 项目默认生成以下结构:WebContent/WEB-INF。将public 文件夹从您的WEB-INF 目录中移出到WebContent 目录中。

在客户端

通过以下方式引用您的静态文件:

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

这是我的reference.

【讨论】:

【参考方案12】:

我遇到了同样的问题,我是这样解决的:

以下内容已添加到 web.xml 文件中:

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

spring3 MVC servlet bean定义文件(如applicationContext.xml,web.xml中配置为contextConfigLocation的文件)添加了以下内容:

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

【讨论】:

【参考方案13】:

就我而言,一切都很好。但是我的控制器有问题

那是我的问题 @RequestMapping(method = RequestMethod.GET)

为此改变:

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

它可以工作

查找具有错误@RequestMappgin 的控制器并进行更改。

【讨论】:

以上是关于SpringMVC 是不是可以配置为处理所有请求,但排除静态内容目录?的主要内容,如果未能解决你的问题,请参考以下文章

处理静态资源

JavaWEB项目静态资源访问问题

SpringMvc分析

05.SpringMVC之请求映射

告诉你:如何花样使用SpringMVC

SpringMVC常用注解