.htaccess的中文URL重写初步研究

标签:无

今天又申请了一个免费主机,本以为支持Python,结果发现根本没有Python的影子,只有没接触过的Perl和CGI,无奈只好再次用起PHP来。
然而由于PHP目前仍没有内置支持unicode,要进行中文URL重写非常麻烦,只好去研究.htaccess了。

弄了半个小时终于有成果了,先给个例子:http://familyproject.cn/家族计划
这个url是没有经过编码的,访问这个地址会有这几种结果:
对于Chrome,会访问:http://familyproject.cn/%E5%AE%B6%E6%97%8F%E8%AE%A1%E5%88%92。(即UTF-8编码)
对于设置了使用UTF-8传递URL的Firefox和IE也会访问上面的网址。
对于未设置使用UTF-8传递URL的Firefox,在简体中文系统上会访问:http://familyproject.cn/%BC%D2%D7%E5%BC%C6%BB%AE。(即GBK编码)
对于未设置使用UTF-8传递URL的IE,在简体中文系统上会访问:http://familyproject.cn/家族计划。注意家族计划这4个字是不能直接传递的,而是编码成16进制的\xbc\xd2\xd7\xe5\xbc\xc6\xbb\xae(GBK编码)。

要让Apache或PHP识别那么多种形式是非常困难的。虽然也想过用^.*$来匹配所有URL,再让PHP去urldecode($_SERVER['REQUEST_URI']);但decode完仍然不知道是UTF-8还是GBK编码,也没法转换编码,因此完全没用。(对于Python来说,这只是小case,一行代码就解决了。)
然而我在测试中注意到这样一件事,我在.htaccess里写了下面几句,并将其保存成UTF-8编码:
RewriteRule ^%BC%D2%D7%E5%BC%C6%BB%AE$ test.php?a=1
RewriteRule ^%E5%AE%B6%E6%97%8F%E8%AE%A1%E5%88%92$ test.php?a=2
RewriteRule ^家族计划$ test.php?a=3
并在test.php中将$_SERVER['REQUEST_URI']和a输出,之后便开始访问http://familyproject.cn/家族计划
结果发现只有Chrome能够打开(Firefox和IE均关闭了UTF-8传递URL),$_SERVER['REQUEST_URI']是UTF-8编码,但输出的a居然是3而不是2。

于是我怀疑mod_rewrite会自动urldecode,便把.htaccess的编码格式改成了ANSI(由于操作系统的内码原因,实际上是GBK),再次测试。
这次的结果出乎我的意料,不但Firefox可以正常访问了,连IE都正常了,当然Chrome就找不到网页了。
结果很明显了:mod_rewrite会将URL进行urldecode,再和.htaccess进行匹配(可以当成是2进制的)。而同样的字符串在相同字符集下进行二进制比较当然是可以匹配的。

这样事情就好办了,我只要把UTF-8编码的字符串强行以GBK来解码,这样mod_rewrite便能正确识别了。
这个转换过程用Python是非常方便的:
>>> print u'家族计划'.encode('u8')
瀹舵棌璁″垝

于是最终的.htaccess文件就是这样了(GBK编码):
RewriteEngine On

RewriteRule ^家族计划$ index.html
RewriteRule ^瀹舵棌璁″垝$ index.html
对于GBK编码访问的浏览器,将会匹配第一条,而UTF-8编码访问的浏览器将会匹配第二条。

或许你会问为什么不把GBK编码的字符串强行以UTF-8来解码,然后保存成UTF-8格式。
原因有三:
  1. GBK编码的文件比UTF-8小。
  2. 把GBK编码的字符串强行以UTF-8来解码会导致转换失败。
  3. Python的print函数只能输出unicode,或以平台的内码来解释string,而无法以UTF-8编码来解释string。
细想一下就会发现其中仍有一个缺陷:在GBK编码中,一个汉字会被编码成2个字节,而UTF-8是3个。所以如果被编码的字符串不是偶数个,那么就会剩下一个字节无法被GBK编码。
例如:
>>> print u'家'.encode('u8')
实际上并不会与UTF-8编码的匹配,因为它少了1个字节。
好在EmEditor也支持16进制视图,所以我查看了一下的编码:
>>> u'家'.encode('u8')
'\xe5\xae\xb6'
把原来的e5 ae(瀹)改成e5 ae b6,再次访问,发现成功了。

当然,这种方式是非常麻烦的,只能用于少数几个页面,除非你用程序来生成.htaccess,但这比PHP处理更加麻烦了…或许Perl解决比较方便,但是还得去调用PHP页面,感觉很头晕…

最后,Google是收录UTF-8编码的,百度是收录GBK编码,目前我还不知道这种方法的收录情况如何。

2条评论 你不来一发么↓ 顺序排列 倒序排列

    向下滚动可载入更多评论,或者点这里禁止自动加载

    想说点什么呢?