Servlet,用于提供静态内容

时间:2020-03-06 14:42:31  来源:igfitidea点击:

我在两个不同的容器(Tomcat和Jetty)上部署了一个webapp,但是它们用于提供静态内容的默认servlet具有处理我要使用的URL结构的不同方式(详细信息)。

因此,我希望在Web应用程序中包含一个小型Servlet,以提供其自己的静态内容(图像,CSS等)。 Servlet应该具有以下属性:

  • 没有外部依赖
  • 简单可靠
  • 支持If-Modified-Since标头(即自定义getLastModified方法)
  • (可选)支持gzip编码,etag,...

这样的servlet在某处可用吗?我能找到的最接近的是servlet书中的示例4-10.

更新:如果我们想知道,我想使用的URL结构很简单:

<servlet-mapping>
            <servlet-name>main</servlet-name>
            <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>/static/*</url-pattern>
    </servlet-mapping>

因此,所有请求都应传递到主servlet,除非它们是针对"静态"路径的。问题是Tomcat的默认servlet没有考虑ServletPath(因此它在主文件夹中查找静态文件),而Jetty则考虑了(因此它在" static"文件夹中)。

解决方案

我遇到了同样的问题,并通过使用Tomcat代码库中的"默认servlet"代码解决了该问题。

http://svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/catalina/servlets/DefaultServlet.java

DefaultServlet是在Tomcat中提供静态资源(jpg,html,css,gif等)的servlet。

这个servlet非常有效,并且具有我们上面定义的一些属性。

我认为此源代码是启动和删除不需要的功能或者功能的好方法。

  • 可以删除对org.apache.naming.resources包的引用,或者将其替换为java.io.File代码。
  • 对org.apache.catalina.util包的引用实际上只是可以在源代码中重复的实用程序方法/类。
  • 可以内联或者删除对org.apache.catalina.Globals类的引用。

我最终滚动了自己的StaticServlet。它支持If-Modified-Since,gzip编码,并且还应该能够从war文件中提供静态文件。这不是很困难的代码,但也不是完全无关紧要的。

该代码可用:StaticServlet.java。随时发表评论。

更新:Khurram询问在StaticServlet中引用的ServletUtils类。这只是我用于项目的带有辅助方法的类。我们唯一需要的方法是coalesce(与SQL函数COALESCE相同)。这是代码:

public static <T> T coalesce(T...ts) {
    for(T t: ts)
        if(t != null)
            return t;
    return null;
}

使用org.mortbay.jetty.handler.ContextHandler。我们不需要诸如StaticServlet之类的其他组件。

在码头的家

$ cd上下文

$ cp javadoc.xml static.xml

$ vi static.xml

...

<Configure class="org.mortbay.jetty.handler.ContextHandler">
<Set name="contextPath">/static</Set>
<Set name="resourceBase"><SystemProperty name="jetty.home" default="."/>/static/</Set>
<Set name="handler">
  <New class="org.mortbay.jetty.handler.ResourceHandler">
    <Set name="cacheControl">max-age=3600,public</Set>
  </New>
 </Set>
</Configure>

使用URL前缀设置contextPath的值,并将resourceBase的值设置为静态内容的文件路径。

它为我工作。

在这种情况下,不需要完全自定义实现默认servlet,可以使用以下简单servlet将请求包装到容器的实现中:

package com.example;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class DefaultWrapperServlet extends HttpServlet
{   
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        RequestDispatcher rd = getServletContext().getNamedDispatcher("default");

        HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
            public String getServletPath() { return ""; }
        };

        rd.forward(wrapped, resp);
    }
}

请参阅JSOS中的StaticFile:http://www.servletsuite.com/servlets/staticfile.htm

我在网络上找到了一些很好的解决方法教程。它简单高效,我在多个项目中使用了REST urls样式方法:

http://www.kuligowski.pl/java/rest-style-urls-and-url-mapping-for-static-content-apache-tomcat,5

我通过扩展tomcat DefaultServlet(src)并覆盖了getRelativePath()方法来做到这一点。

package com.example;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.servlets.DefaultServlet;

public class StaticServlet extends DefaultServlet
{
   protected String pathPrefix = "/static";

   public void init(ServletConfig config) throws ServletException
   {
      super.init(config);

      if (config.getInitParameter("pathPrefix") != null)
      {
         pathPrefix = config.getInitParameter("pathPrefix");
      }
   }

   protected String getRelativePath(HttpServletRequest req)
   {
      return pathPrefix + super.getRelativePath(req);
   }
}

...这是我的servlet映射

<servlet>
    <servlet-name>StaticServlet</servlet-name>
    <servlet-class>com.example.StaticServlet</servlet-class>
    <init-param>
        <param-name>pathPrefix</param-name>
        <param-value>/static</param-value>
    </init-param>       
</servlet>

<servlet-mapping>
    <servlet-name>StaticServlet</servlet-name>
    <url-pattern>/static/*</url-pattern>
</servlet-mapping>

我使用FileServlet取得了不错的效果,因为它几乎支持所有HTTP(标记,分块等)。

要处理来自Spring应用程序的所有请求以及/favicon.ico和/ WEB-INF / jsp / *中Spring的AbstractUrlBasedView将请求的JSP文件,我们只需重新映射jsp servlet和默认servlet:

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

  <servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>/WEB-INF/jsp/*</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/favicon.ico</url-pattern>
  </servlet-mapping>

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

我们不能依赖于jsp servlet的标准映射上的* .jsp url-pattern,因为在检查任何扩展名映射之前,路径模式'/ *'是匹配的。将jsp servlet映射到更深的文件夹意味着它首先被匹配。匹配" /favicon.ico"恰好发生在路径模式匹配之前。更深的路径匹配或者完全匹配都可以使用,但是扩展名匹配不能使其超过" / *"路径匹配。将" /"映射到默认servlet似乎不起作用。我们可能会认为确切的'/'会击败springapp上的'/ *'路径模式。

上面的过滤器解决方案不适用于来自应用程序的转发/包含的JSP请求。为了使其工作,我必须直接将过滤器应用于springapp,此时url-pattern匹配是无用的,因为所有发送到应用程序的请求也都转到了其过滤器。因此,我向过滤器添加了模式匹配,然后了解了" jsp" servlet,并发现它不会像默认servlet那样删除路径前缀。那解决了我的问题,虽然不完全相同,但很常见。

我想出了一个略有不同的解决方案。有点hack-ish,但这是映射:

<servlet-mapping>   
    <servlet-name>default</servlet-name>
    <url-pattern>*.html</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>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
</servlet-mapping>

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

基本上,这只是将所有内容文件按扩展名映射到默认servlet,而其他所有内容都映射到" myAppServlet"。

它可以在Jetty和Tomcat中使用。