文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>Apache的mod_rewrite模块介绍

Apache的mod_rewrite模块介绍

时间:2006-11-23  来源:redaug

mod_rewrite模块

mod_rewrite模块[/color:7064b9ad95][/b:7064b9ad95]可以操作URL的所有部分(包括路径信息部分), 在服务器级的(httpd.conf)和目录级的(.htaccess)配置都有效,还可以生成最终请求串。此重写操作的结果可以是内部子处理,也可以是外部请求的转向, 甚至还可以是内部代理处理。

    但是,所有这些功能和灵活性带来一个问题,那就是复杂性, 因此,不要指望一天之内就能看懂整个模块。

[b:7064b9ad95]内部处理[/b:7064b9ad95]
    [b:7064b9ad95]mod_rewrite模块[/color:7064b9ad95][/b:7064b9ad95]的内部处理极为复杂,但是,为了使一般用户避免犯低级错误, 也让管理员能充分利用其功能,在此仍然做一下说明。

[b:7064b9ad95]API程序段[/b:7064b9ad95]
首先,你必须了解,Apache是通过若干程序段来处理HTTP请求的。 Apache API 对每个程序段提供了一个hook程序。 Mod_rewrite使用两个hook程序: 其一是,URL到文件名的转译hook,用在读取HTTP请求之后,而在授权开始之前;其二是,修正hook,用在授权程序段和读取目录级配置文件(.htaccess)之后, 而在内容处理器激活之前。

所以,Apache收到一个请求并且确定了响应主机(或者是虚拟主机)之后,重写引擎即开始执行URL到文件名程序段,以处理服务器级的配置中所有的mod_rewrite指令。在最终数据目录确定以后,进入修正程序段并触发目录级配置中的mod_rewrite指令。这两个程序段并不是泾渭分明的,但都实施把URL重写成新的URL或者文件名。 虽然API最初不是为此设计的,但它已经成为API的一种用途,而在Apache 1.x 中这是mod_rewrite唯一的实现方法。 记住以下两点,会有助于更好地理解:

虽然mod_rewrite可以重写URL为URL,重写URL为文件名,甚至重写文件名为文件名,但是目前API只提供一个URL到文件名的hook。 在Apache 2.0 中,增加了两个丢失hook以使处理过程更清晰。 但是,这样做并没有给用户带来麻烦,只需记住这样一个事实: Apache借助URL到文件名的hook而比API设计的目标功能更强大。  
难以置信的是,mod_rewrite提供了目录级的URL操作,即,.htaccess文件,而这些文件必须在URL转换成文件名以后的较多步骤完成之后才会被处理。这也是必须的,因为.htaccess文件存在于文件系统中,所以处理已经到达这个层面。换句话说,根据API程序段,这时再处理任何URL操作已经太晚了。 为了解决这个鸡和蛋的问题,mod_rewrite使用了一个技巧:在进行一个目录级的URL/文件名的操作时,mod_rewrite先把文件名重写回相应的URL (通常这个操作是不可行的,但是参考下面的RewriteBase指令就明白它是怎么实现的),然后,对这个新的URL建立一个新的内部的子请求,以此重新开始API程序段的执行。  
另外,mod_rewrite尽力使这些复杂的操作对用户全透明,但仍须记住:服务器级的URL操作速度快而且效率高,而目录级的操作由于这个鸡和蛋的问题速度慢效率也低。但从另一个侧面看,这却是mod_rewrite得以为一般用户提供(局部限制的)URL操作的唯一方法。

牢记这两点!

[b:7064b9ad95]规则集的处理[/b:7064b9ad95]
当mod_rewrite在这两个程序段中开始执行时,它会读取配置结构中的配置好的 (或者是在服务启动时建立的服务器级的,或者是Apache核心在遍历目录采集到的目录级的)规则集,随后,启动URL重写引擎来处理(带有一个或多个条件)的规则集。无论是服务器级的还是目录级的规则集,都是由同一个URL重写引擎处理,只是处理结果不同而已。

规则集中规则的顺序是很重要的,因为重写引擎是按一种特殊的(非常规的)顺序处理的, 其原则是:逐个遍历每个规则(RewriteRule directives), 如果出现一个匹配条件的规则,则可能回头遍历已有的规则条件(RewriteConddirectives)。由于历史的原因,条件规则是置前的,所以控制流程略显冗长,细节见Figure 1。

Figure 1:The control flow through the rewriting ruleset  

可见,URL首先与每个规则的Pattern匹配, 如果匹配不成功,mod_rewrite立即终止此规则的处理,继而处理下一个规则。如果匹配成功,mod_rewrite寻找响应的规则条件,如果一个条件都没有,则简单地用Substitution构造的新的值来替换URL,然后继续处理其他规则。 如果条件存在,则开始一个内部循环按其列出的顺序逐个处理。对规则的条件的处理有所不同:URL并不与pattern匹配,而是,首先通过扩展变量、反向引用、查找映射表等步骤建立一个TestString的字符串,随后,用它来与CondPattern匹配。如果匹配不成功,则整个条件集和对应的规则失败; 如果匹配成功,则执行下一个规则直到所有条件执行完毕。如果所有条件得以匹配,则以Substitution替换URL,并且继续处理。

[b:7064b9ad95]特殊字符的引用[/b:7064b9ad95]
在Apache 1.3.20, TestString and Substitution 字符串中的特殊字符可以用前缀的斜杠来实现转义(即,忽略其特殊含义而视之为普通字符)。比如,Substitution可以用'\$'来包含一个美元符号, 以避免mod_rewrite把它视为反向引用。

[b:7064b9ad95]正则表达式的反向引用能力[/b:7064b9ad95]
这是很重要的一点:一旦在Pattern或者CondPattern使用了圆括号, 就会建立内部的反向引用,可以使用$N和%N来调用(见下述),并且,在Substitution和TestString中都有效。 Figure 2 说明了反向引用被转换扩展的位置。

Figure 2: The back-reference flow through a rule.  

虽然mod_rewrite内部处理的这个过程是比较杂乱的, 但是了解这些可以帮助你阅读下文中指令的讲述。

[b:7064b9ad95]环境变量[/b:7064b9ad95]
[b:7064b9ad95]mod_rewrite 模块[/color:7064b9ad95][/b:7064b9ad95]会跟踪两个额外的(非标准的)CGI/SSI环境变量, SCRIPT_URL和SCRIPT_URI。 他们包含了当前资源的逻辑的网络状态, 而标准的CGI/SSI变量SCRIPT_NAME和 SCRIPT_FILENAME包含的是物理的系统状态。

注意: 这些变量保持的是其最初被请求时的URI/URL, 即, 在任何重写操作之前的。 其重要性在于他们是重写操作重写URL到物理路径名的原始依据。

举例
SCRIPT_NAME=/sw/lib/w3s/tree/global/u/rse/.www/index.html
SCRIPT_FILENAME=/u/rse/.www/index.html
SCRIPT_URL=/u/rse/
SCRIPT_URI=http://en1.engelschall.com/u/rse/
  

[b:7064b9ad95]实用方案[/b:7064b9ad95]
我们还提供另外一个文档URL Rewriting Guide, 列举了许多基于URL的问题的实用方案,其中你可以找到真实有用的规则集和mod_rewrite的更多信息。

[b:7064b9ad95]RewriteBase 指令[/b:7064b9ad95]

RewriteBase指令显式地设置了目录级重写的基准URL。在下文中,你可以看见RewriteRule可以用于目录级的配置文件中(.htaccess),并在局部范围内起作用,即,规则实际处理的只是剥离了本地路径前缀的一部分。 处理结束后,这个路径会被自动地附着回去。默认值是,RewriteBase physical-directory-path

在对一个新的URL进行替换时,[b:7064b9ad95]mod_rewrite模块 [/color:7064b9ad95][/b:7064b9ad95]必须把这个URL重新注入到服务器处理中。为此,它必须知道其对应的URL前缀或者说URL基准。通常,此前缀就是对应的文件路径。但是,大多数网站URL不是直接对应于其物理文件路径的,因而一般不能做这样的假定! 所以在这种情况下,就必须用RewriteBase指令来指定正确的URL前缀。

如果你的网站服务器URL不是与物理文件路径直接对应的, 而又需要使用RewriteRule指令, 则必须在每个对应的.htaccess文件中指定RewriteBase。  
举例,目录级配置文件内容如下:

#
#  /abc/def/.htaccess -- per-dir config file for directory /abc/def
#  Remember: /abc/def is the physical path of /xyz, i.e., the server
#            has a 'Alias /xyz /abc/def' directive e.g.
#
  
RewriteEngine On
  
#  let the server know that we were reached via /xyz and not
#  via the physical path prefix /abc/def
RewriteBase   /xyz
  
#  now the rewriting rules
RewriteRule   ^oldstuff\.html$  newstuff.html
  
上述例子中,对/xyz/oldstuff.html 的请求被正确地重写为物理的文件/abc/def/newstuff.html.

For Apache Hackers
以下列出了内部处理的详细步骤:

Request:
   /xyz/oldstuff.html
  
Internal Processing:
   /xyz/oldstuff.html     -> /abc/def/oldstuff.html  (per-server Alias)
   /abc/def/oldstuff.html -> /abc/def/newstuff.html  (per-dir    RewriteRule)
   /abc/def/newstuff.html -> /xyz/newstuff.html      (per-dir    RewriteBase)
   /xyz/newstuff.html     -> /abc/def/newstuff.html  (per-server Alias)
  
Result:
   /abc/def/newstuff.html
  
虽然这个过程看来很繁复,但是由于目录级重写的到来时机已经太晚了,它不得不把这个(重写)请求重新注入到Apache核心中,所以Apache内部确实是这样处理的。但是:它的开销并不象看起来的那样大,因为重新注入完全在Apache服务器内部进行, 而且这样的过程在Apache内部也为其他许多操作所使用。所以,你可以充分信任其设计和实现是正确的。

[b:7064b9ad95]RewriteCond 指令[/b:7064b9ad95]

RewriteCond指令定义了一个规则的条件,即,在一个RewriteRule指令之前有一个或多个RewriteCond指令。 条件之后的重写规则仅在当前URI与pattern匹配并且符合这些条件的时候才会起作用。

TestString是一个纯文本的字符串,但是还可以包含下列可扩展的成分:

RewriteRule反向引用: 引用方法是  
$N  

(0 <= N <= 9) 引用当前(带有若干RewriteCond指令的)RewriteRule中的 与pattern匹配的分组成分(圆括号!)。  
RewriteCond反向引用: 引用方法是  
%N  

(1 <= N <= 9) 引用当前若干RewriteCond条件中最后符合的条件中的分组成分(圆括号!)。  
RewriteMap 扩展: 引用方法是  
${mapname:key|default}  

细节请参见the documentation for RewriteMap。  
服务器变量: 引用方法是  
%{ NAME_OF_VARIABLE }  

NAME_OF_VARIABLE可以是下表列出的字符串之一: HTTP headers: connection & request:   
HTTP_USER_AGENT
HTTP_REFERER
HTTP_COOKIE
HTTP_FORWARDED
HTTP_HOST
HTTP_PROXY_CONNECTION
HTTP_ACCEPT
REMOTE_ADDR
REMOTE_HOST
REMOTE_USER
REMOTE_IDENT
REQUEST_METHOD
SCRIPT_FILENAME
PATH_INFO
QUERY_STRING
AUTH_TYPE
   
server internals: system stuff: specials:  
DOCUMENT_ROOT
SERVER_ADMIN
SERVER_NAME
SERVER_ADDR
SERVER_PORT
SERVER_PROTOCOL
SERVER_SOFTWARE
TIME_YEAR
TIME_MON
TIME_DAY
TIME_HOUR
TIME_MIN
TIME_SEC
TIME_WDAY
TIME
API_VERSION
THE_REQUEST
REQUEST_URI
REQUEST_FILENAME
IS_SUBREQ
  

这些都对应于类似命名的HTTP MIME头、Apache服务器的C变量以及Unix系统中的 struct tm字段,大多数都在其他的手册或者CGI规范中有所讲述。 而其中为mod_rewrite所特有的变量有:

IS_SUBREQ  
如果正在处理的请求是一个子请求,它包含字符串"true",否则就是"false"。 模块为了解析URI中的附加文件,有可能会产生子请求。  
API_VERSION  
这是正在使用的httpd中(服务器和模块之间内部接口)的Apache模块API的版本, 其定义位于include/ap_mmn.h中。[b:7064b9ad95]mod_rewrite模块[/color:7064b9ad95][/b:7064b9ad95]版本对应于正在使用的Apache的版本 (比如,在Apache 1.3.14的发行版中,这个值是19990320:10)。 通常,对它感兴趣的是模块的作者。  
THE_REQUEST  
这是由浏览器发送给服务器的完整的HTTP请求行。(比如, "GET /index.html HTTP/1.1"). 它不包含任何浏览器发送的附加头信息。  
REQUEST_URI  
这是在HTTP请求行中所请求的资源。(比如上述例子中的"/index.html".)  
REQUEST_FILENAME  
这是与请求相匹配的完整的本地文件系统的文件路径名或描述.  
特别注意事项:

SCRIPT_FILENAME和REQUEST_FILENAME包含的值是相同的,即, Apache服务器的内部request_rec结构中的filename字段。 第一个其实就是大家都知道的CGI变量名,而第二个则是( 包含了request_rec结构中的uri字段的)REQUEST_URI的一个副本,  
特殊形式: %{ENV:variable} 其中的variable可以是任何环境变量。 它是通过查找Apache内部结构得到的, 或者(如果没找到的话)是由Apache服务器进程通过getenv()得到的。  
特殊形式: %{HTTP:header} 其中的header可以是任何HTTP MIME头的名称。 它是通过查找HTTP请求得到的。比如: %{HTTProxy-Connection}就是HTTP头 ``Proxy-Connection:''的值.  
特殊形式 %{LA-U:variable} 它是一个预设的值, variable的最终值在执行一个内部的(基于URL的)子请求后决定。在重写需要使用一个尚未有效的但是会在之后的API程序段中设置的变量的时候,就会使用这个方法。比如,需要在服务器级配置(httpd.conf文件)中重写REMOTE_USER变量,则,必须使用%{LA-U:REMOTE_USER},因为此变量是由认证程序段设置的,而这个程序段是在mod_rewrite所在的URL转译程序段之后才执行的。但是,因为mod_rewrite是通过API修正程序段来实现目录级(.htaccess file)配置的,而这个程序段在认证程序段之前就执行了,所以用%{REMOTE_USER}就可以了。  
特殊形式: %{LA-F:variable} 它是一个预设的值, variable的最终值在执行一个内部的(基于文件名的)子请求后决定。 大多数情况下和上述的LA-U是相同的.  
CondPattern是条件pattern, 即, 一个应用于当前实例TestString的正则表达式, 即, TestString将会被计算然后与CondPattern匹配.

谨记: CondPattern是一个兼容perl的正则表达式, 但是还有若干增补:

可以在pattern串中使用'!' 字符(惊叹号)来实现匹配的反转。  
CondPatterns有若干特殊的变种。除了正则表达式的标准用法,还有下列用法:  
'<CondPattern' (词典顺序的小于)
将CondPattern视为纯字符串,与TestString以词典顺序相比较. 如果按词典顺%
相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载