正文
开发中突然用户反馈一个问题,说无法搜索英文名,研究后发现URL会对空格自动截断引发的。
例如你本意在网页中的js上想访问实现的是:
http://www.sample.com?name=angle lucy
然后现实却是:
http://www.sample.com?name=angle
我们需要对URL中的特殊字符实体化,也就是用字符实体。
方法有js原生的方法:escape()
、 encodeURI()
、 encodeURIComponent()
, 相反是 unescape()
、 decodeURI()
、 decodeURIComponent()
。
修改后发现又出现了中文前后端编码不统一的问题。还是用post表单提交吧,下面一步一步说一下表单相关的问题。
我们GET提交时,请求头中Content-Type:text/html; charset=UTF-8
,POST请求时描述表单编码格式的类型描述常见的有:
Content-Type:application/x-www-form-urlencoded
Content-Type:multipart/form-data;
Content-Type:application/json
等等。
Content-Type 表单上传编码格式
text/html
Content-Type:text/html; charset=UTF-8
,GET请求时url中的空格会编码为%20
,中文等也会编码为类似%E6%B5%8B%E8%AF%95
的字符(里面是测试二字)。
application/x-www-form-urlencoded
application/x-www-form-urlencoded
,是浏览器默认的表单上传编码格式,参数的格式为key=value&key2=value2
,
将键值对的参数用&
间隔连接起来,如果有空格,将空格转换为+
加号;有特殊符号,将特殊符号转换为ASCII HEX值(如&
间隔符本身、中文等)
如 name=a li,nick=白杨,id=98815 编码后表单内容为:
Content-Type: application/x-www-form-urlencoded
name=a+li&nick=%E7%99%BD%E6%9D%A8&id=98815
multipart/form-data
如果表单上传编码格式为 multipart/form-data
,服务器怎么知道每个参数的开始位置和结束位置呢?
看一个请求头示例:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="name"
a li
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="nick"
白杨
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="id"
98815
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="/home/ali/Desktop/2112.png"
------WebKitFormBoundary7MA4YWxkTrZu0gW--
可以看出multipart/form-data
不会对参数编码,使用的boundary(分割线),相当于&
,boundary的值是----Web***
。
最后连接成一串字符传输。
上传文件等二进制也要指定编码格式为multipart/form-data:
<form action="http://localhost:8888/testFile" enctype="multipart/form-data" method="POST">
<input type="file" name="file">
<input type="submit" value="提交">
</form>
application/json
application/json 也是现在比较常用的表单编码格式,如:
Content-Type: application/json
{
"name":"a li",
"nick":"白杨",
"id":98815,
"items":[
{
"item_id":101
}
]
}
Postman查看代码请求实现方式
也是在上面的学习中,发现 Postman 有各种语言实现web请求的代码示例。在Postman新建请求后,点击右侧Send下面一行的Code, 可选择各种语言代码实现,如PHP cURL:
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "http://www.ali.com",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{\n \"name\":\"a li\",\n \"nick\":\"白杨\",\n \"id\":98815\n}",
CURLOPT_HTTPHEADER => array(
"Content-Type: application/json",
"Postman-Token: 4585896a-8cd5-4cc2-bdb1-171b496408bf",
"cache-control: no-cache"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
PHP HttpRequest:
<?php
$request = new HttpRequest();
$request->setUrl('http://www.ali.com');
$request->setMethod(HTTP_METH_POST);
$request->setHeaders(array(
'Postman-Token' => '022cb6be-c3f0-4889-a306-24537e67c53f',
'cache-control' => 'no-cache',
'Content-Type' => 'application/json'
));
$request->setBody('{
"name":"a li",
"nick":"白杨",
"id":98815
}');
try {
$response = $request->send();
echo $response->getBody();
} catch (HttpException $ex) {
echo $ex;
}
PHP pecl_http:
<?php
$client = new http\Client;
$request = new http\Client\Request;
$body = new http\Message\Body;
$body->append('{
"name":"a li",
"nick":"白杨",
"id":98815
}');
$request->setRequestUrl('http://www.ali.com');
$request->setRequestMethod('POST');
$request->setBody($body);
$request->setHeaders(array(
'Postman-Token' => 'e227c298-9df3-4099-9373-5789288fd631',
'cache-control' => 'no-cache',
'Content-Type' => 'application/json'
));
$client->enqueue($request)->send();
$response = $client->getResponse();
echo $response->getBody();
字符实体
HTML 转义字符串(Escape Sequence)也称字符实体(Character Entity)。
在HTML中,定义转义字符串的原因有两个:
第一个原因是像“<”和“>”这类符号已经用来表示HTML标签,因此就不能直接当作文本中的符号来使用。 为了在HTML文档中使用这些符号,就需要定义它的转义字符串。当解释程序遇到这类字符串时就把它解释为真实的字符。 在输入转义字符串时,要严格遵守字母大小写的规则。
第二个原因是,有些字符在ASCII字符集中没有定义,因此需要使用转义字符串来表示。
转义字符串(Escape Sequence),即字符实体(Character Entity)分成三部分: 第一部分是一个&符号,英文叫ampersand;第二部分是实体(Entity)名字或者是#加上实体(Entity)编号;第三部分是一个分号。
escape、encodeURI和encodeURIComponent的区别。 escape是对字符串(string)进行编码(而另外两种是对URL),作用是让它们在所有电脑上可读。 对URL编码是常见的事,所以这两个方法应该是实际中要特别注意的。 它们都是编码URL,唯一区别就是编码的字符范围,其中
escape方法不会对下列字符编码: ASCII字母、 数字、 * @ - _ + . /
encodeURI方法不会对下列字符编码: ASCII字母、 数字、 ~!@#$&*()=:/,;?+'
encodeURIComponent方法不会对下列字符编码: ASCII字母、 数字、 ~!*()'
也就是encodeURIComponent编码的范围更广,会将http://XXX
中的//
也编码,会导致URL不可用。
https://www.w3school.com.cn/js/jsref_escape.asp
https://www.w3school.com.cn/js/jsref_encodeuri.asp
https://www.w3school.com.cn/js/jsref_encodeURIComponent.asp
URLEncode函数, encodeURIComponent封装修改实现:
function urlencode(str) {
str = (str + '').toString();
return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
}
php的编码:
urlencode — 编码 URL 字符串,与 WWW 表单 POST 数据的编码方式是一样的,同时与 application/x-www-form-urlencoded 的媒体类型编码方式一样。
urldecode — 解码已编码的 URL 字符串
htmlentities — 将字符转换为 HTML 转义字符, 如果要解码(反向操作),可以使用 html_entity_decode()。
htmlspecialchars — 将特殊字符转换为 HTML 实体
看一个实例,PHP 后台处理 JSON_encode 参数有空格,JS 输出变 空格 变 +
(加号)问题:
// php 端处理
$arr = array();
$arr['status'] = urlencode('正 常');
$jsonRack = json_encode($arr,true);
$jsonRack = str_replace('+', '%20', $jsonRack); // 出来 空格变 + 的问题
// js 前端处理
var phpJson = JSON.parse( phpJson );
var status = decodeURI(map[i].status); // 空格就会正常显示了
URL和URI的区别
URI包括URL和URN两个类别,URL是URI的子集,所以URL一定是URI,而URI不一定是URL
URI = Universal Resource Identifier 统一资源标志符,用来标识抽象或物理资源的一个紧凑字符串。
URL = Universal Resource Locator 统一资源定位符,一种定位资源的主要访问机制的字符串,一个标准的URL必须包括:protocol、host、port、path、parameter、anchor。
URN = Universal Resource Name 统一资源名称,通过特定命名空间中的唯一名称或ID来标识资源。
题外话
下句是题外话,与上面无半点关系。
4月29日,看着没做多少事,然而人却是繁忙劳累的,五一假期前会议、定期版本上线、快速修复临时反馈的一个系统问题、 查看一个抛过来的看不懂语义的sql语句的问题、临时介入一个项目是否可做、帮忙查找push有没有发送到指定人、 翻看一个陌生项目的代码理出可能逻辑等等。想一下应该是今天与人的语言接触太多了吧,利益、成见, 还有就是对自己个性丧失的无可奈何,3年前我是这样的性格吗?为了有一个生活来源对他人俯首帖耳、唯唯诺诺,唯恐出一点儿错。 一个人的死亡是从失去个性那一刻开始的,而我们却美其名为成熟。 我需要解放自己了,不能再这种环境下彻底沉沦下去。有话就说,放开犯错。我要个性突出,要有多样色彩!
突然想起了霍光,一生谨小慎微,连每天的步子都一样规则、一样大小,绝不迈错一步;卫青也是,只做好分内事,职责之外不发一言、从不越矩, 汉武帝对其都深有意见,然而无可奈何,不然太子刘据的事情上据理直言说上几句,也可以避免日后悲剧的发生。然而反观霍去病,他却飞扬跋扈, 敢怒敢争,擅自杀死李广之子李敢。为什么同样是汉武帝的手下,性格的差距却如此悬殊。可能是因为童年的经历吧,霍光和卫青的少年时期一直生活在社会底层; 而霍去病却生活在平阳侯府,不用考虑和面对吃饭问题。另外一方面可能是因为汉武帝太强势,霍光和卫青又分外珍惜现在来之不易的身份和名声,所以怕犯错, 所以不敢为。
一个公司的发展是需要敢为的人的,做公司就是在做管理,让想做事的人大胆做事。
一个国家的强大是需要敢为的人的,不敢为则只能守成,无开创性事业。
参考资料
HTML字符实体(Character Entities),转义字符串(Escape Sequence) https://www.cnblogs.com/mengyuxin/p/5970687.html
HTML URL 编码 https://www.w3school.com.cn/tags/html_ref_urlencode.html
Post请求的两种编码格式:application/x-www-form-urlencoded和multipart/form-data https://www.jianshu.com/p/53b5bd0f1d44
escape、encodeURI和encodeURIComponent的区别 https://www.cnblogs.com/qlqwjy/p/9934706.html
JS中URL中的特殊字符问题:escape,encodeURI,encodeURIComponent https://www.cnblogs.com/wwzhang/p/4707677.html
完美的js URLEncode函数 https://www.cnblogs.com/leekenky/p/4628379.html
PHP 手册 函数参考 其它基本扩展 URLs URL 函数 urlencode https://www.php.net/urlencode
PHP 后台处理 JSON_encode 参数有空格,JS 输出变 空格 变 + (加号)问题 https://learnku.com/articles/24886
URL和URI的区别 https://www.cnblogs.com/hust-ghtao/p/4724885.html
中国历史上第一个废掉皇帝的大臣是怎么炼成的? https://baike.baidu.com/tashuo/browse/content?id=aeb9b7bbc56f5d7b3a272003&lemmaId=419055&lemmaId=419055&fr=qingtian