Nodejs如何处理IE诡异的非英文URL编码


众所周知URL里是不能出现除了英文数字和某些特殊符号外的其他字符的,也不能出现汉字。URL会出现汉字也就4种情况:

  1. 网址路径(path)中包含汉字 :如 https://zh.wikipedia.org/wiki/浏览器
  2. Get方法生成包含汉字的URL :一般是由表单生成的,比如 https://zh.wikipedia.org/w/index.php?search=%E6%B5%8F%E8%A7%88%E5%99%A8
  3. 查询字符串(Query String)包含汉字 :比如 https://zh.wikipedia.org/w/index.php?search=浏览器 与第二种不同的是直接在浏览器地址栏输入汉字
  4. Ajax调用的URL包含汉字 : 比如 <script>url = url + "?q=" +document.getElementById("input").value;</script>

在第一种情况下,各浏览器始终使用UTF-8编码,也就是最后被解析成了 https://zh.wikipedia.org/wiki/%E6%B5%8F%E8%A7%88%E5%99%A8
第二种情况则是根据当前页面编码进行转义,2中的例子用的就是UTF-8
第三种情况就相当诡异了,Chrome和FF都使用UTF-8进行转义,而IE则不是。查了一下,IE用的是操作系统的默认编码,据我所知这个默认编码在不同Windows版本和不同系统语言下都不一样。
第四种情况和第三类似,IE使用系统默认而Chrome和FF使用UTF-8。

于是乎服务器拿到第三和第四种的请求后,根本不知道用的是什么编码。第四种倒容易解决,提前用JavaScript指定编码转义一次就好。但是第三种就...

于是说服务器如何确定在第三种情况发来的发来的查询字符串真正内容?这里用的是Nodejs,可以的话也想听听PHP的解决方案。

试了一个貌似在IE下,用第三种情况搜索Wordpress也会返回404(用错误解码方式的关键词查询数据库没有找到结果)。

=============
突然想到一个点子,能不能从请求header的UA判断是否为IE,如果是再通过Accept-Language来猜测编码...(我开始乱来了....

encoding urlencode node.js php

214215 10 years, 4 months ago

这个问题和IE没多大关系...
btw: 你后面提到的 通过Accept-Language来猜测编码 更是不靠谱.
因为这个 请求头是 告诉服务器 浏览器支持什么样的 语言(Language) , 与本次提交时参数的编码没有任何关系.

你遇到的问题, 主要是后端的编码识别的问题.

测试代码:
1.html 文档声明内容编码为 utf-8 , 且文件保存编码为 utf-8 .
不管是在IE还是Chrome,Firefox下, 点按钮提交的汉字均为 utf-8 编码.


 <!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8" />
 </head>

 <body>


<form action="http://www.baidu.com/s" method="GET">
  <input type="text" name="wd" value="浏览器"/>
  <input type="submit" />
  </form>


 </body>
</html>

2.html 文档声明内容编码为 gb2312 , 且文件保存编码为 gb2312 .
不管是在IE还是Chrome,Firefox下, 点按钮提交的汉字均为 gb2312 编码.


 <!DOCTYPE html>
<html>
 <head>
  <meta charset="gb2312" />
 </head>

 <body>


<form action="http://www.baidu.com/s" method="GET">
  <input type="text" name="wd" value="浏览器"/>
  <input type="submit" />
  </form>


 </body>
</html>

上面两种编码提交到 www.baidu.com 进行搜索时, 百度均可识别出来正确的汉字.

GB2312 编码时的URL地址: http://www.baidu.com/s?wd=%E4%AF%C0%C0%C6%F7
UTF-8 编码时的URL地址: http://www.baidu.com/s?wd=%E6%B5%8F%E8%A7%88%E5%99%A8

认清楚问题之后, 就可以去找正确的答案了:
百度搜索关键字 PHP 汉字 编码 识别 (Google被墙,所以只能用百度代替了)
由编码识别遇到问题,思考utf8编码正则表达式(php版本)

将上面的测试代码的 action 指向下面这个 php 文件.
你会发现不管是 GB2312 编码提交过来的数据, 还是 UTF-8 编码提交过来的数据, 都可以正确显示所提交的汉字.


 <?php

header('Content-Type: text/html; charset=utf-8');

$wd = $_GET['wd'];

if(checkUtf8($wd) == 0){
    $wd = iconv('gbk', 'utf-8', $wd);
}

echo $wd;



function checkUtf8($str,$extzh=1)
{
    ///utf8编码正则检测函数
    ///copyright qq:8292669
    ///author  程默  http://www.cnblogs.com/chengmo

    //gbk,utf8重叠的范围是:[c0-df][a0-bf] 这块字符在utf8中有,在gbk编码没有对应字符因此向gbk转换会出现"?"号
    if($extzh==1)
    {
        $re='/^([\x01-\x7f]|[\xc0-\xdf][\xa0-\xbf])+$/';  ///这部分字符如果当作utf8处理,在转换为gbk时候就会出现问题"?"号。因此直接返回不为utf8
        if(preg_match($re,$str))  ///公共字符验证成功
        {
            return 0;  ///不是utf8
        }
    }
    $re='/^([\x01-\x7f]|[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3}|[\xf8-\xfb][\x80-\xbf]{4}|[\xfc-\xfd][\x80-\xbf]{5})+$/';
    return preg_match($re,$str);
}

clipboard.png

clipboard.png

这里是以PHP为例, nodejs 与此类似.

三过福利而不入 answered 10 years, 4 months ago

Your Answer