自己在项目开发中写了个自用接口,用GET方法传参(用户名和密码)。最近收到用户反馈:密码为特殊字符时会出错。我一开始想是不是php的mysql_real_escape_string函数将特殊字符转义了,但用户说自己密码只有“+”号这个特殊字符,而“+”号不属于该函数转义的范围之内。
为了弄明白这个问题我就在本地测试了一下,果然发现有bug。
测试代码:
1 2 3 4 5 |
<?php echo $_GET['a']."<br />"; echo $_GET['b']."<br />"; echo urldecode($_GET['a'])."<br />"; ?> |
测试的url为http://localhost/test/?a=123!@#&b=123结果报错了:
可以看出“#”号后面的参数被屏蔽了,这很正常,因为“#”号后面的数据不会发送到HTTP请求中。
当url为http://localhost/test/?a=123!@+1&b=123输出为:
1 2 3 |
123!@ 1 123 123!@ 1 |
可以看出“+”号变成了空格。google了一下找到个解决方案,就是发送数据前先urlencode一下,然后后台再解码。基于这个思路我又试了一下,这次的url为:http://localhost/test/?a=123!%40%23%26%2b&b=123,输出为:
1 2 3 |
123!@#&+ 123 123!@#& |
这里就有问题了,按理说urldecode后输出的才是正确的,网上提供的方法都是这样写的。但事实却不是这样,应该是GET方法获取值之后会自动urldecode,自己再urldecode就画蛇添足了(“+”号比较特殊,urldecode之后会变成空格)。所以我们使用GET方法时只需要将参数urlencode一次就行了,简单处理一下就可解决问题,也不会遇到“+”号的bug。
使用POST方法就不会出现这些问题,因为POST方法会对数据进行编码,其中就包括urlcode。但也不是完全不会出现这些问题,在使用curl模拟POST方法时还是会出现这种问题。
测试代码:
1 2 3 4 5 |
<?php echo $_POST['a']."<br />"; echo $_POST['b']."<br />"; echo urldecode($_POST['a'])."<br />"; ?> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php $a = "123!+@#&"; $post_data = "a=$a&b=123"; //POST值 // 1. 初始化 $ch = curl_init(); // 2. 设置选项 curl_setopt($ch, CURLOPT_URL, "http://localhost/test/index.php"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); // 3. 执行并获取HTML文档内容 $text = curl_exec($ch); // 4. 释放curl句柄 curl_close($ch); echo $text; ?> |
运行curl.php的结果:
1 2 3 |
123! @# 123 123! @# |
可以很清楚的看见,虽然没有像GET方法那样“#”号后面的数据都被忽略了。但获得的数据还是不正确,这是因为curl中POST的值也是像GET方法一样书写(参数字符串)。其实POST的值也可以写成数组的形式,但那是在提交文件流的时候使用(Content-Type头将会被设置成multipart/form-data),这里(提交数据)使用数组的话会出错。参照GET方法中的解决方案,urlencode一下参数值就行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php $a = "123!+@#&"; $a = urlencode($a); // url编码,处理特殊字符 $post_data = "a=$a&b=123"; //POST值 // 1. 初始化 $ch = curl_init(); // 2. 设置选项 curl_setopt($ch, CURLOPT_URL, "http://localhost/test/index.php"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); // 3. 执行并获取HTML文档内容 $text = curl_exec($ch); // 4. 释放curl句柄 curl_close($ch); echo $text; ?> |
输出:
1 2 3 |
123!+@#& 123 123! @#& |
当然,服务端也不用urldecode。
有时网上的那些解决方案并不是都正确,几乎都是复制来复制去的,完全没有考究,自己在实践过程中要注意辨别。
附一个流传很广的解决方案:
1 、改用POST方法,ok。
2 、在js里用url = encodeURI(encodeURI(XXX)),后台再解码一次ok。
3 、将参数里的加号进行转换data = data.replace(/+/g, "%2B");
特别是第二种方案,编码两次,解码一次,太脑残了。完全就是为了解决问题而解决问题。