URI网址空格实体化小记


开发中突然用户反馈一个问题,说无法搜索英文名,研究后发现URL会对空格自动截断引发的。


正文

开发中突然用户反馈一个问题,说无法搜索英文名,研究后发现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


返回