blob: dcc216f0e6fcc139108cf25b018d37a41a4f4ae8 [file]
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE manualpage SYSTEM "../style/manualpage.dtd">
<?xml-stylesheet type="text/xsl" href="../style/manual.zh-cn.xsl"?>
<!-- English Revision: 1933438:1935430 (outdated) -->
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manualpage metafile="tech.xml.meta">
<parentdocument href="./">Rewrite</parentdocument>
<title>Apache mod_rewrite 技术细节</title>
<summary>
<p>本文档讨论 <module>mod_rewrite</module> 和 URL 匹配的一些技术细节。</p>
</summary>
<seealso><a href="../mod/mod_rewrite.html">模块文档</a></seealso>
<seealso><a href="intro.html">mod_rewrite 简介</a></seealso>
<seealso><a href="remapping.html">重定向和重映射</a></seealso>
<seealso><a href="access.html">访问控制</a></seealso>
<seealso><a href="vhosts.html">虚拟主机</a></seealso>
<seealso><a href="proxy.html">代理</a></seealso>
<seealso><a href="rewritemap.html">使用 RewriteMap</a></seealso>
<seealso><a href="advanced.html">高级技术</a></seealso>
<seealso><a href="avoid.html">何时不使用 mod_rewrite</a></seealso>
<section id="InternalAPI"><title>API 阶段</title>
<p>Apache HTTP 服务器分多个阶段处理请求。在每个阶段,
一个或多个模块可能被调用来处理请求生命周期的那个部分。
这些阶段包括 URL 到文件名转换、身份验证、授权、内容和日志记录等。
(这不是一个详尽的列表。)</p>
<p><module>mod_rewrite</module> 在其中两个阶段(或通常所说的"钩子")
中起作用,以影响 URL 的重写方式。</p>
<p>首先,它使用 URL 到文件名转换钩子,该钩子在 HTTP
请求被读取之后、但在任何授权开始之前触发。其次,它使用 Fixup 钩子,
该钩子在授权阶段之后、在目录级配置文件(<code>.htaccess</code> 文件)
被读取之后、但在内容处理程序被调用之前触发。</p>
<p>当请求进入并确定了对应的服务器或虚拟主机后,
重写引擎开始处理出现在服务器级配置中的所有 <module>mod_rewrite</module>
指令。(即在主服务器配置文件和 <directive module="core"
type="section">Virtualhost</directive> 配置段中。)
这发生在 URL 到文件名转换阶段。</p>
<p>几个步骤之后,一旦找到最终的数据目录,
就会应用目录级配置指令(<code>.htaccess</code> 文件和 <directive
module="core" type="section">Directory</directive> 块)。
这发生在 Fixup 阶段。</p>
<p>在每种情况下,<module>mod_rewrite</module> 都会将
<code>REQUEST_URI</code> 重写为新的 URL 或文件名。</p>
<p>在目录级上下文中(即在 <code>.htaccess</code> 文件和
<code>Directory</code> 块中),这些规则在 URL
已经被转换为文件名之后才被应用。因此,<module>mod_rewrite</module>
最初用来与 <directive module="mod_rewrite">RewriteRule</directive>
指令进行比较的 URL 路径是转换后的完整文件系统路径,
其中当前目录路径(包括尾部斜杠)已从前面被移除。</p>
<p>举例来说:如果规则位于 /var/www/foo/.htaccess 中,
正在处理对 /foo/bar/baz 的请求,则表达式 ^bar/baz$ 将会匹配。</p>
<p>如果在目录级上下文中进行了替换,将使用新的 URL
发出新的内部子请求,重新开始请求阶段的处理。如果替换是相对路径,
<directive module="mod_rewrite">RewriteBase</directive>
指令决定在替换前面添加的 URL 路径前缀。在目录级上下文中,
必须注意创建的规则最终(在未来某轮目录级重写处理中)不会执行替换,
以避免循环。(参见 <a
href="https://cwiki.apache.org/confluence/display/httpd/RewriteLooping">RewriteLooping</a>
以进一步讨论此问题。)</p>
<p>由于在目录级上下文中对 URL 进行了进一步操作,
你需要注意在该上下文中以不同方式编写重写规则。特别要记住,
前导目录路径将从你的重写规则所看到的 URL 中被去除。
请参考下面的示例以进一步说明。</p>
<table border="1">
<tr>
<th>规则位置</th>
<th>规则</th>
</tr>
<tr>
<td>VirtualHost 配置段</td>
<td>RewriteRule "^/images/(.+)\.jpg" "/images/$1.gif"</td>
</tr>
<tr>
<td>文档根目录下的 .htaccess 文件</td>
<td>RewriteRule "^images/(.+)\.jpg" "images/$1.gif"</td>
</tr>
<tr>
<td>images 目录下的 .htaccess 文件</td>
<td>RewriteRule "^(.+)\.jpg" "$1.gif"</td>
</tr>
</table>
<p>要更深入地了解 <module>mod_rewrite</module> 如何在不同上下文中操作 URL,
你应该查阅重写期间生成的<a
href="../mod/mod_rewrite.html#logging">日志条目</a></p>
</section>
<section id="InternalRuleset"><title>规则集处理</title>
<p>现在当 <module>mod_rewrite</module> 在这两个 API 阶段被触发时,
它从其配置结构中读取已配置的规则集(该配置结构在服务器级上下文中于启动时创建,
或在目录级上下文中于 Apache 内核的目录遍历过程中创建)。
然后使用包含的规则集(一条或多条规则及其条件)启动 URL 重写引擎。
URL 重写引擎本身的操作在两种配置上下文中完全相同。
只有最终结果的处理方式不同。</p>
<p>规则集中规则的顺序很重要,因为重写引擎以一种特殊的(且不太直观的)
顺序处理它们。规则如下:重写引擎逐条遍历规则集中的规则
<directive module="mod_rewrite">RewriteRule</directive> 指令),
当某条规则匹配时,它会可选地遍历对应的条件
<code>RewriteCond</code> 指令)。由于历史原因,
条件写在前面,因此控制流程有点绕。参见图 1 了解更多细节。</p>
<p class="figure">
<img src="../images/rewrite_process_uri.png"
alt="RewriteRule 和 RewriteCond 匹配流程" /><br />
<dfn>图 1:</dfn>重写规则集的控制流程
</p>
<p>首先,URL 与每条规则的<em>模式</em>进行匹配。如果匹配失败,
<module>mod_rewrite</module> 立即停止处理该规则,并继续处理下一条规则。
如果<em>模式</em>匹配成功,<module>mod_rewrite</module>
会查找对应的规则条件(RewriteCond 指令,
在配置中紧接在 RewriteRule 之前出现)。
如果没有条件,则用从字符串<em>替换</em>构造的新值替换 URL,
并继续其规则循环。但如果存在条件,则启动一个内部循环,
按照条件列出的顺序处理它们。对于条件,逻辑是不同的:
我们不是将模式与当前 URL 匹配。而是首先通过展开变量、
反向引用、映射查找<em></em>来创建一个字符串 <em>TestString</em>
然后尝试将 <em>CondPattern</em> 与之匹配。如果模式不匹配,
则整组条件和对应规则都失败。如果模式匹配,则处理下一个条件,
直到没有更多条件为止。如果所有条件都匹配,
则继续用<em>替换</em>替换 URL。</p>
</section>
</manualpage>