电话:010-64681504 010-64684094
传真:010-64673024
邮箱:888@edo2008.com
手 机: 013693193565
下面的说明只是向比较技术性的读者如网站开发人员和站长介绍重写和转向怎样工作。你也可以路过这些很技术性的讨论。
Apache服务器的mod_rewrite和微软的IIS服务器的ISAPI_Rewrite是重写URL的强有力工具。下面是使用这些有力的一些原因:
一、改变了网站的URL结构,所以内容从一个位置转移到另外一个位置。更换CMS或者因为什么原因改动网站组织结构都可能导致这个结果。
二、想把搜索引擎不友好的URL转换为友好的。
如果你运行Apache网站服务器,需要将被称为重写规则的指令放在.htaccess文件或者Apache配置文件中(例如httpd.conf或sites_conf目录中的特定的网站配置文件)。与此类似,如果你运行IIS服务器,需要使用ISAPI插件如ISAPI_Rewrite,将规则放在httpd.ini配置文件中。
要注意,ISAPI_Rewrite和mod_rewrite的规则可能稍有不同,下面的讨论以mod_rewrite为主。你的.htaccess文件将这样开头:
Rewriteengine on
RewriteBase /
如果你是在服务器配置文件中加入重写规则,应该省略第二行,因为RewriteBase 只在.htaccess文件中被支持。我们在这里使用RewriteBase,这样你就不必在所有规则的开始放上^/,只要用^就可以了。
然后是重写规则。也许你想让对产品页面URL http://www.ws818.com/products/123的请求显示在http://www.ws818.com/get_product.php?id=123,而不必改变用户浏览器地址框中的URL,也不用将get_product.php脚本重新编程。当然,这并不能替换网站所有页面上的链接中的动态URL,那是另外一个问题。你可以用一个重写规则完成第一部分:
RewriteRule ^products/([0-9]+)/?$ /get_product.php?id=$1 [L]
上面的例子告诉服务器,所有向/products/目录的请求应该被映射为对/get_product.php的请求。/products/下的子目录用做PHP脚本的一个参数。
^表示域名后面URL的开始,$表示URL的结尾,[0-9]表示一个数字,紧接其后的+表现出现一个或多个数字。与此类似,紧接着/的?表示出现0个或一个/字符。()表示将括在其中的任何内容放入内存,然后可以通过$1表示存在内存中的内容。同样,如果你在规则中包含了第二个圆括号,就要用$2调用,依此类推。[L]标志告诉重写引擎,如果规则匹配就停止,不然剩下的规则要继续运行。
下面是一个秒微复杂些的例子,URL http://www.ws818.com/webapp/wcs/stores/servlet/productDisplay?storeid=1001&catalogId=1001&langId=-1&categoryID=4&productID=123 要被重写为http://www.ws818.com/4/123.htm。
RewriteRule ^([^/]+)/([^/]+)\.htm$ /webapp/wcs/stores/servlet/productDisplay?storeid=1001&catalogId=1001&langId=-1&categoryID=$1&productID=$2 [QSA,L]
[^/]代表除斜杠以外的任何字符,因为方括号里的^代表“非”。[QSA]用于你不希望查询字符串被拿掉时。
要写好重写规则,你需要成为“模式匹配”专家,下面是一些最重要的特殊字符及重写引擎怎样解读:
* 代表前面0或更多个字符
+ 代表前面1或更多个字符
? 代表前面0或1个字符
^ 代表字符串的开始位置
$ 代表字符串结束的位置
. 代表任何字符
\ 取消后面字符的特殊意思,转为原义字符。例如,\.意思是那个点(.),不代表通配符,而是真正的字符点(.)
^ 在方括号里代表“非”,例如,[^/]代表非斜杠
写正则表达式时极容易出错。导致错误匹配的常见例子包括:
一、应该用.+时却用了.*,因为.*可能匹配0个字符
二、没有和反斜杠\取消特殊字符意义。例如用.没有用\.,而你的意思是“点”的这个字符(.),而不是任何字符
三、认为不需要指明字符串开始或结尾而忽略了^或$
四、使用匹配所有字符出现而不是停留在第一次出现的“贪婪表达式”
解释“贪婪”意思的最好方式是举个例子:
RewriteRule ^(.*)/?index\.html$ /$1/ [L,R=301]
这将把http://www.ws818.com/blah/index.html的请求转向到http://www.ws818.com/blah//。这大概不是想要的结果。为什么会发生这种情况呢?因为.*在/?看到之前就匹配到了斜杠。幸亏很简单就解决。只要用[^或.*?代替.*做匹配。例如,用^(.*?)/?或者[^/]+/[^/]而不是.*/.*。
所以,可以用这个代码更正前面的规则:
RewriteRule ^(.*?)/?index\.html$ /$1/ [L,R=301]
为什么不用下面这个呢?
RewriteRule ^([^/]*)/?index\.html$ /$1/ [L,R=301]
这个比较受限,因为只能匹配有一个目录的URL。包含多个目录的URL的http://www.ws818.com/store/cheese/swiss/wheel/index.html不会被匹配。
可以想象,测试/排错是URL重写的重要部分。排错时,RewriteLog和RewriteLogLevel指令很有帮助。将RewriteLogLevel设为4以上,看看写引擎解析你的规则时执行到了哪里。
前几个例子中的[R=301]告诉重写引擎做301转向而不是标准重写。
还有一个和RewriteRule一起很好用的指令称为RewriteCond。RewriteCond可以用于要匹配查询字符串里的什么东西时,域名或域名与URL中的问号之间没有其他东西。
注意,RewriteRule和RewriteCond都不能使用URL里的锚点部分,也就是#后面的部分,因为它是浏览器内部使用的,不会作为请求的一部分发送到服务器。下面RewriteCond的例子在允许后面的重写规则执行前先寻找匹配主机名:
Rewritecond %{HTTP_HOST} !^www\.ws818\.com% [NC]
RewriteRule ^(.*)$ http://www.ws818.com/$1 [L,R=301]
注意正则表达式开始前的叹号,重写引擎解读为“非”。
只要主机名不是http://www.ws818.com,就301转向到www子域名的对应规范化URL。[NC]使重写条件区分大小写。你可能会问,保留查询字符串的[QSA]在哪?转向时不需要写出来,是隐含的。
做转向时如果你不想在重写规则里保留查询字符串,在规则中目标URL结尾加一个问号,如下:
RewriteCond %{HTTP_HOST} !^www\.ws818\.com$ [NC]
RewriteRule ^(.*)$ http://www.ws818.com/$1? [L,R=301]
为什么不用^ws818\.com$呢?看一下这个:
RewriteCond %{HTTP_HOST} ^ws818\.com$ [NC]
RewriteRule ^(.*)$ http://www.ws818.com/$1? [L,R=301]
这将不会匹配拼错的域名如ws818.com,DNS服务器和虚拟主机都设置为对其反应。
在什么情况下你可能要像前两个例子一样将被转向URL中的查询字符串忽略?有Session ID或跟踪参数需要拿掉时。转向后保留跟踪参数即区必要,从规范化角度来说也不好。如果你想把被转向URL中的跟踪参数忽略但保留查询字符串的其他参数怎么办?这是处理静态URL的方法:
RewriteCond %{QUERY_STRING} ^source=[a-z0-9]*$
RewriteRule ^(.*)$ /$1? [L,R=301]
这是动态URL:
RewriteCond %{QUERY_STRING} ^(.+)&source=[a-z0-9]+(&?.*)$
RewriteRule ^(.*)$ /$1?%1%2 [L,R=301]
需要在转向前通过cookie玩点花样?调用cookie脚本然后转向到规范化URL:
RewriteCond %{QUERY_STRING} ^source=([a-z0-9]*)$
RewriteRule ^(.*)$ /cookiefirst.php?source=%1&dest=$1 [L]
注意上面代码中没有[R=301],这是有的。没必要让用户看到脚本。使用重写,让脚本完成任务后自己做301转向。
其他值得通过重写规则和[R=301]矫正的规范化问题包括搜索引擎索引HTTPS下的分类页面,以及URL最后缺少本来应该有的斜杠。首先,解决HTTPS:
#redirect online catalog pages in the /catalog/ directory if HTTPS
RewriteCond %{HTTPS} on
RewriteRule ^catalog/(.*) http://www.ws818.com/catalog/$1 [L,R=301]
注意,如果你的加密服务器与主服务器是分开的,可以省略RewriteCond那行。
现在加上最后的斜杠:
rewriteRule ^(.*[^/])$ /$1/ [L,R=301]
完成从动态URL到静态URL的重写项目后,逐步删除动态URL时不仅要替换网站上所有旧URL,还要将旧的动态URL做301转向到对应的静态URL,因此确保被索引的、被讨论的、被链接的、被加入书签的是新URL,旧URL将被索引库删除。一般来说,下面是实现方法:
RewriteCond %{QUERY_STRING} id=([0-9]+)
RewriteRule ^get_product\.php$ /products/%1.html? [L,R=301]
然而,不小心的话会导致递归转向的无限循环。一个避免这种情况的简单办法是给重写目标URL加一个无意义的参数,并在转向前确保这个无意义参数不存在:
RewriteCond %{QUERY_STRING} ID=([0-9]+)
RewriteCond %{QUERY_STRING} !blah=blah
RewriteRule ^get_proudct\.php$ /products/%1.html? [L,R=301]
RewriteRule ^products/([0-9]+)/?$ /get_product.php?id=$1&blah=blah [L]
注意这个例子用于两个Rewritecond行,一个在另一个上面。同一段代码是一起出现的所有转向条件被“加”在一起,也就是“和”逻辑。如果要条件之间“或”逻辑,要使用[OR]标签。
上一篇:为什么以及何时让URL转向
下一篇:URL转向和重写方法