分析DispatcherServlet拦截"/"造成静态文件404

来源:互联网 发布:返利平台源码 编辑:程序博客网 时间:2024/05/20 20:43
今天在项目中访问静态文件一直404,后来在web.xml里面加入了这样一段代码,就能够访问了。
  <servlet-mapping>    <servlet-name>default</servlet-name>    <url-pattern>*.html</url-pattern>  </servlet-mapping>

这里以html为例,如果需要多种静态资源类型,就需要配置多个以上。

为什么配置这个就能访问了呢?default是哪个servlet?为什么要使用这个servlet处理静态资源?我的脑海中产生这样一系列的问题。

总的来说,是因为配置了spring MVC的DispatcherServlet拦截“/”,为了实现REST风格,拦截了所有的请求,同时,静态文件也被拦截。 所以激活Tomcat的defaultServlet来处理静态文件。

我们接着刚刚的疑问继续探究。

先在项目里面找了default,没有找到。很好,百度吧!百度说这是他tomcat默认的serlvet,那这个servlet是干什么用的呢?我们已经知道它的一个用处了,就是处理静态资源。从这个网址可以看到官方文档里面对DefaultServlet的解释。http://tomcat.apache.org/tomcat-7.0-doc/default-servlet.html#what

1. What is the DefaultServlet?

DefaultServlet是用来处理静态资源,就像处理目录列表一样。

2. Where is it declared?

DefaultServlet是在$CATALINA_BASE/conf/web.xml里面做全局声明的,默认情况下是这样子的

    <servlet>        <servlet-name>default</servlet-name>        <servlet-class>          org.apache.catalina.servlets.DefaultServlet        </servlet-class>        <init-param>            <param-name>debug</param-name>            <param-value>0</param-value>        </init-param>        <init-param>            <param-name>listings</param-name>            <param-value>false</param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>...    <servlet-mapping>        <servlet-name>default</servlet-name>        <url-pattern>/</url-pattern>    </servlet-mapping>
所以通常情况下,DefaultServlet是在web项目启动时加载的,同时debug调试和文件目录都是禁用的。这里的listings是指文件目录,可是文件目录又是什么呢?别着急,我们继续往下看。

3. What can I change?

DefaultServlet允许下面这些初始化参数

  • debug 这个参数对tomcat的开发者用处比较大,对我们而言用处不大。可选的值有0,1,11,1000.[0]
  • listings 如果没有设置欢迎页,那么当我们访问项目名时,会显示文件目录吗?这个参数的值是true或者false.[false].(这里我把值设置成了true,并且删除了欢迎文件,访问项目时,显示了文件目录可以对比我的项目目录) 欢迎文件也是servlet api的一部分。警告:包含很多实体的文件目录是非常珍贵的。大量的对大文件列表的请求,十分消耗服务器资源。
  • readmeFile 如果设置了显示文件列表,readme文件可能也会显示在列表里面。这个文件是插入的,它可能包含html.
  • globalXsltFile 如果你想定制你的文件目录,你可以使用XSL来转换。这个的值也是一个相对文件的名称($CATALINA_BASE/conf/ or $CATALINA_HOME/conf/),这个将会给所有的文件目录使用。但是这个可以被每一个context或者directory覆盖。具体的请看下面的contextXsltFile和localXsltFile.
  • contextXsltFile 你可以通过配置contextXsltFile来定制你的文件目录。但是一定要使用上下文的相对路径,并且是一.xsl或者.xslt为扩展名的文件(例如:/path/to/context.xslt)。这个会覆盖globalXsltFile的值. 如果配置的文件路径不存在,仍然会使用globalXsltFile的配置。如果默认的globalXsltFile文件也不存在,那会直接显示文件目录。
  • localXsltFile 你同样可以通过配置localXsltFile来定制你的文件目录。它一定是以.xsl或.xslt为扩展名的在目录列表里面的文件。并且这个配置能覆盖globalXsltFile和contextXsltFile的配置。如果配置的这个值找不到对应文件,就会使用contextXsltFile的配置,如果contextXsltFile也找不到,就会使用globalXsltFile的配置,如果都找不到,那么就会展示默认的文件目录。
  • input 当读取资源文件时,服务器写入缓冲区的大小,单位为bytes.[2048]
  • output 当写入资源文件时,服务器输出缓冲区大小,单位为bytes.[2048]
  • readonly 上下文是否是只读的,HTTP的命令,像PUT,DELETE这些是不是被拒绝的。[true]
  • fileEncoding 读取静态资源时的文件编码。[platform default]
  • sendfileSize 如果使用的连接器支持sendfile,这意味着以KB大小的小文件将会被使用。通常使用负数来禁止sendfile.[48]
  • useAcceptRanges 如果设置为true,当响应时请求头的Accept-Ranges将会被设置。[true]
  • showServerInfo 当展示文件列表是,服务器信息是否在响应时一起发送给客户端。[true]
4. How do I customize directory listings?
第三点中的globalXsltFile,localXsltFile,contextXsltFile这几个参数都是用来定制文件目录的,那么具体怎么定制呢?
我们可以重新定义一个Servlet来继承DefaultServlet,重写里面的方法,并在web.xml里面生命。就是说,tomcat开发人员假定我们是可以读到DefaultServlet的代码的,并且可以做适当的调整。如果我们无法读到DefaultServlet的代码,那这种方法就不适用了。
我们也可以使用localXsltFile和globalXsltFile来配置,DefaultServlet将会根据localXsltFile和globalXsltFile的配置找到xsl转换文件,创建并运行一个xml文件。localXsltFile优先级最高,然后是globalXsltFile,最后是默认的行为,格式如下:
    <listing>     <entries>      <entry type='file|dir' urlPath='aPath' size='###' date='gmt date'>        fileName1      </entry>      <entry type='file|dir' urlPath='aPath' size='###' date='gmt date'>        fileName2      </entry>      ...     </entries>     <readme></readme>    </listing>
  • 如果设置type='dir',就不用设置size
  • Readme是一个CDATA,不由xml解析器解析
下面这个xsl文件示例,是mimics默认的tomcat行为:
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="3.0">  <xsl:output method="html" html-version="5.0"    encoding="UTF-8" indent="no"    doctype-system="about:legacy-compat"/>  <xsl:template match="listing">   <html>    <head>      <title>        Sample Directory Listing For        <xsl:value-of select="@directory"/>      </title>      <style>        h1 {color : white;background-color : #0086b2;}        h3 {color : white;background-color : #0086b2;}        body {font-family : sans-serif,Arial,Tahoma;             color : black;background-color : white;}        b {color : white;background-color : #0086b2;}        a {color : black;} HR{color : #0086b2;}        table td { padding: 5px; }      </style>    </head>    <body>      <h1>Sample Directory Listing For            <xsl:value-of select="@directory"/>      </h1>      <hr style="height: 1px;" />      <table style="width: 100%;">        <tr>          <th style="text-align: left;">Filename</th>          <th style="text-align: center;">Size</th>          <th style="text-align: right;">Last Modified</th>        </tr>        <xsl:apply-templates select="entries"/>        </table>      <xsl:apply-templates select="readme"/>      <hr style="height: 1px;" />      <h3>Apache Tomcat/8.0</h3>    </body>   </html>  </xsl:template>  <xsl:template match="entries">    <xsl:apply-templates select="entry"/>  </xsl:template>  <xsl:template match="readme">    <hr style="height: 1px;" />    <pre><xsl:apply-templates/></pre>  </xsl:template>  <xsl:template match="entry">    <tr>      <td style="text-align: left;">        <xsl:variable name="urlPath" select="@urlPath"/>        <a href="{$urlPath}">          <pre><xsl:apply-templates/></pre>        </a>      </td>      <td style="text-align: right;">        <pre><xsl:value-of select="@size"/></pre>      </td>      <td style="text-align: right;">        <pre><xsl:value-of select="@date"/></pre>      </td>    </tr>  </xsl:template></xsl:stylesheet>

5. How do I secure directory listings?
在每一个web项目里面使用web.xml.你可以去看servlet文档的安全模块。

以上,都是tomcat官网上关于DefaultServelt的说明,我们了解DefaultServelt使用方法,相关参数的配置。那么具体怎么来处理静态资源的呢?还是不知道。
直接来看代码吧。。。

我们可以看到DefaultServlet继承了HttpServlet,刚刚那些参数都是DefaultServlet里面的属性,并且在构造器里面设置了默认值。

/*      */ private static final long serialVersionUID = 1L;/*      */ private static final DocumentBuilderFactory factory;/*      */ private static final SecureEntityResolver secureEntityResolver;/*      */ protected int debug;/*      */ protected int input;/*      */ protected boolean listings;/*      */ protected boolean readOnly;/*      */ protected int output;/*      */ protected static final URLEncoder urlEncoder;/*      */ protected String localXsltFile;/*      */ protected String contextXsltFile;/*      */ protected String globalXsltFile;/*      */ protected String readmeFile;/*      */ protected transient ProxyDirContext resources;/*      */ protected String fileEncoding;/*      */ protected int sendfileSize;/*      */ protected boolean useAcceptRanges;/*  227 */ protected static final ArrayList<Range> FULL = new ArrayList();/*      */ protected boolean showServerInfo;/*      */ protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY";/*      */ protected static final String RESOURCES_JNDI_NAME = "java:/comp/Resources";/*      */ protected static final StringManager sm;/*      */ protected static final int BUFFER_SIZE = 4096;/*      *//*      */ public DefaultServlet()/*      */ {/*  144 */ this.debug = 0;/*      *//*  150 */ this.input = 2048;/*      *//*  156 */ this.listings = false;/*      *//*  162 */ this.readOnly = true;/*      *//*  168 */ this.output = 2048;/*      *//*  180 */ this.localXsltFile = null;/*      *//*  186 */ this.contextXsltFile = null;/*      *//*  192 */ this.globalXsltFile = null;/*      *//*  198 */ this.readmeFile = null;/*      *//*  204 */ this.resources = null;/*      *//*  211 */ this.fileEncoding = null;/*      *//*  217 */ this.sendfileSize = 49152;/*      *//*  222 */ this.useAcceptRanges = true;/*      *//*  232 */ this.showServerInfo = true;/*      */ }
接着就是destroy()和init()方法。init()在服务器启动时执行,destroy()方法在服务器停止时执行。在init()方法里面,我们可以看到它是去读取配置文件的这些属性值,并赋值。
/*      */ public void init()/*      */ throws ServletException/*      */ {/*  305 */ if (getServletConfig().getInitParameter("debug") != null) {/*  306 */ this.debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));/*      */ }/*  308 */ if (getServletConfig().getInitParameter("input") != null) {/*  309 */ this.input = Integer.parseInt(getServletConfig().getInitParameter("input"));/*      */ }/*  311 */ if (getServletConfig().getInitParameter("output") != null) {/*  312 */ this.output = Integer.parseInt(getServletConfig().getInitParameter("output"));/*      */ }/*  314 */ this.listings = Boolean.parseBoolean(getServletConfig().getInitParameter("listings"));/*      *//*  316 */ if (getServletConfig().getInitParameter("readonly") != null) {/*  317 */ this.readOnly = Boolean.parseBoolean(getServletConfig().getInitParameter("readonly"));/*      */ }/*  319 */ if (getServletConfig().getInitParameter("sendfileSize") != null) {/*  320 */ this.sendfileSize = (Integer.parseInt(getServletConfig().getInitParameter("sendfileSize"))* 1024);/*      */ }/*      *//*  323 */ this.fileEncoding = getServletConfig().getInitParameter("fileEncoding");/*      *//*  325 */ this.globalXsltFile = getServletConfig().getInitParameter("globalXsltFile");/*  326 */ this.contextXsltFile = getServletConfig().getInitParameter("contextXsltFile");/*  327 */ this.localXsltFile = getServletConfig().getInitParameter("localXsltFile");/*  328 */ this.readmeFile = getServletConfig().getInitParameter("readmeFile");/*      *//*  330 */ if (getServletConfig().getInitParameter("useAcceptRanges") != null) {/*  331 */ this.useAcceptRanges = Boolean.parseBoolean(getServletConfig().getInitParameter("useAcceptRanges"));/*      */ }/*      *//*  334 */ if (this.input < 256)/*  335 */ this.input = 256;/*  336 */ if (this.output < 256) {/*  337 */ this.output = 256;/*      */ }/*  339 */ if (this.debug > 0) {/*  340 */ log(new StringBuilder().append("DefaultServlet.init:  input buffer size=").append(this.input).append(", output buffer size=").append(this.output).toString());/*      */ }/*      *//*  345 */ this.resources = ((ProxyDirContext) getServletContext().getAttribute("org.apache.catalina.resources"));/*      *//*  347 */ if (this.resources == null) {/*      */ try {/*  349 */ this.resources = ((ProxyDirContext) new InitialContext().lookup("java:/comp/Resources"));/*      */ }/*      */ catch (NamingException e)/*      */ {/*  354 */ throw new ServletException("No resources", e);/*      */ }/*      */ }/*      *//*  358 */ if (this.resources == null) {/*  359 */ throw new UnavailableException("No resources");/*      */ }/*      *//*  362 */ if (getServletConfig().getInitParameter("showServerInfo") != null)/*  363 */ this.showServerInfo = Boolean.parseBoolean(getServletConfig().getInitParameter("showServerInfo"));/*      */ }
我猜测getServletConfig().getInitParameter("debug")这两个方法就是读取在web.xml里面配置的default的参数的值的。

那么文件拦截后,交到doGet(),doPost方法处理。我们来看下。。在doGet()里面调用了serveResource(request, response, true)方法。我看了一下这个方法,不是所有的代码都能看懂,但是大致就是获取请求地址,然后处理跳转响应页面。和我们普通的servlet差不多。

最后就是要把这个default的servlet配置到springMVC跳转器的前面,这样请求就会走DefaultServlet了。

以上便是我的理解,欢迎指教!!


阅读全文
2 0