[翻译] URL 重写新手指南
本文由 伯乐在线 – Delostik 翻译,牧师 校稿。未经许可,禁止转载!
英文出处:addedbytes。欢迎加入翻译组。
伯乐在线地址:http://blog.jobbole.com/104898/
URL重写是什么?
大多数动态网站的URL中都含有变量,以告知站点哪些信息需要展示给用户。比如像下面这个URL,会通知相关的脚本加载编号为7的产品:
|
1 |
http://www.example.com/show_a_product.php?product_id=7 |
这种URL结构的问题在于它并不容易记忆。如果是在电话中也很难读出来(令人惊讶的是有很多人通过这种方式传递URL)。搜索引擎和用户都不能从URL中得到有用的内容信息。你没办法从URL看出在这个页面能买挪威的蓝鹦鹉(的羽毛)。这种是相当标准的URL——也就是一般你在CMS网站上看到的那种。相比下面的URL:
|
1 |
http://www.example.com/products/7/ |
这种URL更清晰,也更短,更容易记忆,也更容易被念出来。尽管如此,它还是不能告诉别人它所指向的内容是什么。但是我们可以更进一步:
|
1 |
http://www.example.com/parrots/norwegian-blue/ |
这就是我们要的东西了。即使不看上下文,我们也可以从这个URL中看出你要找的东西就在这个页面上。搜索引擎可以将这个URL分割成单词(搜索引擎会将URL中的连字符当做空格,但是下划线不是),然后根据这些信息更好地判断页面内容。这种URL是易于记忆和传递的。
不幸的是,要让服务器理解最后一种URL,需要我们做一番工作。当URL发起一个请求,服务器需要知道如何处理URL,才能知道该返回给用户什么内容。URL重写就是这种将最后一种URL“翻译”成服务器能理解的语言的技术。
平台和工具
根据你的服务器上运行的软件,你可能已经有URL重写模块。如果没有,大多主机都提供启用或安装相关模块的功能,你可以尝试启用它。
Apache启用URL重写是最简单的,它通常带有自己的内建URL重写模块——mod_rewrite,启动和使用mod_rewrite就像上传和命名文件一样简单。
IIS是微软的服务器软件,标配并不包含URL重写的能力,但是有很多插件提供了这种功能。ISAPI_Rewrite 是我比较推荐一款插件,这是我发现的一款功能最接近mod_rewrite的插件。在这篇文章的最后附有ISAPI_Rewrite的安装和配置说明。
下面的代码是一些使用mod_rewrite的例子。
基本的URL重写
首先我们来看一个简单的例子。我们有一个网站,含有一个单独的PHP脚本,展示了一个单独的页面,URL如下:
|
1 |
http://www.example.com/pet_care_info_07_07_2008.php |
我们想要简化URL,理想的URL像这样:
|
1 |
http://www.example.com/pet-care/ |
为了让这个URL有效,我们需要让服务器在内部将所有的“pet-care”的请求重定向到“pet_care_info_07_07_2008.php”。我们希望这个工作在内部进行是因为我们不希望用户浏览器的地址栏发生改变。
为了达到这个目的,我们首先需要建立一个名为“.htaccess”的文本文档来存储我们的规则。这个文件必须命名成这样(不能是“.htaccess.txt”或者“rules.htaccess”)。这个文件应该放在服务器的根目录(本例中放在与 “pet_care_info_07_07_2008.php” 相同的目录中)。可能那里已经有一个.htaccess文件了,这时我们就应该编辑这个文件而不是覆盖它。
.htaccess文件是服务器的配置文件。如果文件中有错误,服务器会提示错误信息(通常错误代码是500)。如果使用FTP协议向服务器发送文件,必须使用ASCII编码传输,而不是BINARY。在本例中,这个文件有两个作用:1. 通知Apache启动重写引擎; 2. 把我们的重写规则告诉Apache。因此我们需要在这个文件中加入以下内容:
|
1 |
RewriteEngine On # Turn on the rewriting engine RewriteRule ^pet-care/?$ pet_care_info_01_02_2008.php [NC,L] # Handle requests for "pet-care" |
有几个需要注意的地方:在.htaccess文件中,‘#’之后的文字都会被当做注释忽略,建议大家多使用注释;“RewriteEngine”这一行在每个.htaccess文件中应当只使用一次(请注意在后面的代码中都不包括这一行)。
“RewriteRule”行是见证奇迹的地方。这一行可以分为五个部分:
- RewriteRule —— 通知Apache这是一条独立的RewriteRule。
- ^/pet-care/?$ —— 模式串,服务器会检查每一条URL是否跟这个模式串匹配。如果匹配,Apache会使用后面的Substitution块替换URL。
- pet_care_info_01_02_2003.php —— Substitution。如果请求与前面的模式串匹配,则Apache会使用这个URL替换原来的URL。
- [NC,L] —— 标记,告诉Apache如何应用规则。在本例中,我们使用了两个标记:“NC”告诉Apache这条规则不区分大小写;“L”告诉Apache如果这条规则被应用,则不再应用其他规则。
- # Handle requests for “pet-care” —— 注释,用来解释这条规则做了什么(可选,但是建议这样做)
这种规则是重写一个独立URL的简单方式,也是几乎所有URL重写的基础。
模式和替换
前面提到的规则使你可以重定向单独的URL,但是mod_rewrite的强大之处在于根据包涵的模式串,识别和重写成组的URL。
现在我们要把站点上所有的URL改成像前面举得例子中说的那样。你现在的URL是这样的:
|
1 |
http://www.example.com/show_a_product.php?product_id=7 |
你想把它改成这个样子:
|
1 |
http://www.example.com/products/7/ |
我们可以只写一条规则来管理所有产品id,而不用对每个id都写一条规则。其实就是你想把这种类型的URL:
|
1 |
http://www.example.com/show_a_product.php?product_id={a number} |
改成这种样子的:
|
1 |
http://www.example.com/products/{a number}/ |
为了达到这个目的,你需要使用正则表达式。正则表达式是一种格式特殊的、方便服务器理解的pattern。一种典型的用来匹配数字的正则表达式像这样:
|
1 |
[0-9]+ |
方括号内含有一系列的字符,“0-9”表示所有数字。加号表示将会匹配任何加号前出现的pattern——本例中也就是“一个或多个数字”——也就是我们要在URL中寻找的东西。
整个的模式部分一般都会被当做正则表达式进行处理——你不需要启动或激活它们。
|
1 |
RewriteRule ^products/([0-9]+)/?$ show_a_product.php?product_id=$1 [NC,L] # Handle product requests |
首先应该注意的是用括号括起来的模式串,这样我们可以在接下来的Substitution中使用被括号中pattern匹配的URL进行“back-reference”(向后引用)。Substitution中的“$1”告诉Apache将之前被括号里面的pattern匹配到的URL字串放到这里。你可以有很多back-reference,他们按出现顺序编号。
像上面的RewriteRule语句,将会使Apache把所有domain.com/products/{number}/的请求重定向到show_a_product.php?product_id={same number}。
正则表达式
本文要讲的并不是完整的正则表达式的指南。然而,需要注意的重点是整个pattern都会被当做正则表达式处理,要一直注意正则表达式中那些特殊的字符。
最典型的例子是在pattern中使用句号。在一个patter中,’.’ 表示“任意字符”,而不是一个普通的句号,所以当你想要匹配一个句号的时候,你需要对句号进行“转义”——就是在它前面加一个特殊的符号,反斜杠,它会让Apache将下一个字符当做普通字符处理。
比如,下面这条语句 ,将会匹配”rss1xml”、”rss-xml”这样的URL:
|
1 |
RewriteRule ^rss.xml$ rss.php [NC,L] # Change feed URL |
这样做一般不会有什么很严重的问题,但是对字符进行适当转义的习惯对深入学习正则表达式有好处。所以最好的写法应该是这样:
|
1 |
RewriteRule ^rss\.xml$ rss.php [NC,L] # Change feed URL |
这种情况只适用于模式比配,而不能用于替换。此外还有其他的字符(我们称之为“元字符”)用于转义:
- . (任意字符)
- *(零个或多个字符)
- +(一个或多个字符)
- {}(匹配至少、至多个字符)
- ? (非贪婪限制符,跟在任何一个限制符后面表示该
- ! (负向预查,出现在字符串头表示从不匹配该字符串的位置开始匹配)
- ^(匹配输入字符串的开始位置,出现在范围中表示负)
- $(表示字符串结尾)
- [](包含,默认是一个字符长度)
- – (当在方括号中出现时表示范围)
- () (段域)
- | (对两个匹配条件进行逻辑或运算)
- \(将下一个字符转义)
通过使用正则表达式,我们可以匹配任意URL并且重写它们。现在回到我们文章开头提到的例子,我们希望匹配并重写这条URL:
|
1 |
http://www.example.com/parrots/norwegian-blue/ |
我们想要把这条URL翻译成如下的格式交给服务器:
|
1 |
http://www.example.com/get_product_by_name.php?product_name=norwegian-blue |
我们可以用很简单一条规则完成这个工作:
|
1 |
RewriteRule ^parrots/([A-Za-z0-9-]+)/?$ get_product_by_name.php?product_name=$1 [NC,L] # Process parrots |
这个规则让我们可以提取出URL中“parrot/”之后的任意字母、数字和连字符的组合([A-Za-z0-9-])(将连字符放在字符末尾,方括号的最后,使之被当做连字符处理,而不是分隔符),并将匹配到的产品名称替换为$1.
如果需要的话,我们也可以让规则更普适,使得不管产品出现在哪个目录下,都可以发送给相同的脚本,就像:
|
1 |
RewriteRule ^[A- |