网络传输中的那些编码之-URL编码

由于URL编码的一些混乱情况,曾经困扰着无数的程序员,本文将捋一捋URL编码相关的一些知识点,希望看望之后能给人恍然大悟的感觉。

本文将从如下几个方面进行介绍:

  • 什么是URL编码
  • 为什么进行URL编码
  • 对哪些字符进行URL编码
  • 怎么编码
  • 实施URL编码的主体是谁
  • URL编码后都长什么样子

什么是URL编码

首先什么是编码,现实世界中的事务,比如字符和数字要想在计算机中进行展示,必须经过一定形式的转换,也就是要转换成为0,1串的表示方式,这就是编码。在计算机中,编码的本质是为了存储,和传输。比如说字符G使用ASCII 0x47进行编号,并使用一个字节进行存储。一般的,在计算机中对于英文和数字采用的是ASCII来进行存储和传输。如下图1:
在这里插入图片描述
图1
在早期,URL编码,则是将scope限定在URL中,对URL中的一些字符进行编码。但是在实际的使用中,你会看到HTTP的body以及header等其他部分,也都有URL编码的身影。由于这些特殊字符出现在了HTTP body以及header等部分,为了方便传输,直接将URL编码的方法拿来使用,但是在使用的过程中仍然称之为URL编码。

为什么进行URL编码

在计算机发展的早期,使用ASCII来表示已经能够满足要求。随着网络的普及以及网络所承载应用的复杂化,在URL出现了越来越多的非字母和数字,比如说汉字等非英文字符,空格,大括号,大于号小于号,甚至是一些不可见字符等特殊字符。由于软件的异构性,操作系统的异构性等各方面的差异,导致各系统在解码数据的时候存在着不同,举例如下:

  • 场景一:当A向B传输的URL中包含空格20的时候,这个空额究竟是URL的一部分,还是分隔符呢?如下:
GET /32k.html HTTP/1.1

可以看出空格在HTTP协议中是各个字段的分隔符,将请求方法GET,请求的路径/32k.html,以及请求的协议HTTP/1.1进行分割,因此如果在请求的路径中出现空格,势必对于解码会造成影响。因此如果请求路径中包含空格,则需要特殊的处理,即URL编码。

  • 场景二:汉字无法使用ASCII进行表示,可以是使用UTF-8或者GBK编码表示,例如汉字村的GBK编码为B4E5,当甲向乙请求的URL包含汉字村的时候,在URL没有任何任何标识的情况下,乙是将B4E5当作B4和E5两个单独的字节进行处理还是将其当作B4E5进行处理呢?此处存在着歧义性。

对于上述两种场景,URL中出现的字符已经使得URL本身解析出现歧义。URL编码正是为了解决这种歧义而来,对于汉字等非英文字符,以及空格等特殊字符进行编码。

对URL哪些字符进行编码

究竟哪些字符需要编码,哪些字符不需要呢。可以知道,常见的英文字符以及数字直接使用ASCII的形式表示就可以了,不需要进行URL编码,对于特殊的字符以及非英文字符是否都需要URL编码呢?这是在网络通信协议是有响应的规定和建议的,对应的RFC为rfc1738,如下:

Thus, only alphanumerics, the special characters “$-_.+!*'(),”, and reserved characters used for their reserved purposes may be used unencoded within a URL.

可以看到对于不需要编码的字符包括数字,字母以及常见的特殊字符和保留字符,当然关于保留字符的定义RFC使用了关键词may,即可能,这就导致可程序员在实现URL编码的过程中出现了步调不一致的情况。当然RFC中针对为啥大于号以及中括号等是需要编码的也给出了解释,如下:

Characters can be unsafe for a number of reasons. The space character is unsafe because significant spaces may disappear and insignificant spaces may be introduced when URLs are transcribed or typeset or subjected to the treatment of word-processing programs. The characters “<” and “>” are unsafe because they are used as the delimiters around URLs in free text; the quote mark (“”") is used to delimit URLs in some systems. The character “#” is unsafe and should always be encoded because it is used in World Wide Web and in other systems to delimit a URL from a fragment/anchor identifier that might follow it. The character “%” is unsafe because it is used for encodings of other characters. Other characters are unsafe because gateways and other transport agents are known to sometimes modify such characters. These characters are “{”, “}”, “|”, “”, “^”, “~”, “[”, “]”, and “`”.
All unsafe characters must always be encoded within a URL. For example, the character “#” must be encoded within URLs even in systems that do not normally deal with fragment or anchor identifiers, so that if the URL is copied into another system that does use them, it will not be necessary to change the URL encoding.

主要是出于系统异构性的考虑,比如大于号,小于号以及引号在某些系统中被当做分隔符进行使用。但是RFC同时也提到字母和数字也可以编码,如下:

On the other hand, characters that are not required to be encoded (including alphanumerics) may be encoded within the scheme-specific part of a URL, as long as they are not being used for a reserved purpose.

其实RFC就是这样,只是给出了一个指导性的大纲,有的地方并没有定的很绝对,除了must的字段,对于may,should等表述,在最终工程落地来看,不同的实现人员是存在不同的思路的。这也就导致到了后续究竟是对那些字符进行编码,哪些不进行编码,其实并没有统一。因此现实中的不同语言,不同软件对于URL编码处理并不一致,程序员也会不断的遇到URL编码的各种奇怪的状况。

怎么编码

总的来说,对URL中需要编码的字符,使用%加上该字符对应的编码值。例如空格对应的ASCII值是20,对于URL出现的空格需要使用%20进行替换,即传输的实际值为0x25 0x32 0x30,如图3所示:
在这里插入图片描述
图3
汉字存在多种编码方式,例如GBK以及UTF-8等,汉字村中少年的GBK编码是B4E5(46309)、D6D0(54992)、C9D9(51673)、C4EA(50410),村UTF8编码是E69D91。因此如果村采用的是GBK编码,实际传输的字符是%B4%E5;如果传输使用的是UTF8编码,实际传输的字符是%E6%9D%91,如图4所示:
在这里插入图片描述

图4
对于标准的ASCII范围内的值,由于基本上所有的编码方式,都是向前兼容,因此这些字符对应的URL编码存在差异的情况不多。但是对于汉字来说,URL编码中使用的具体编码方式采用的是GBK或者GB2312还是UTF-8能,或者是其他的编码方式呢?这就要看那个主体对于传输的字符进行URL编码。

实施URL编码的主体是谁

总的来说,对于传输字符进行URL编码既可以是网页服务的提供者,例如百度和Google,也可以是浏览器的开发商,例如IE和chrome等。对于汉字来说因为GBK是国标编码方式,是为汉字编码方式而生,因此在涉及到汉字或者中国软件公司的地方,采用GBK的情况居多,但是越来越多的程序员开始使用UTF-8的方式。UTF-8是为了全球字符能够在计算机中存储和传输指定的编码方式,因此在以美国为代表的软件公司中使用UTF-8的编码方式居多。同样的日本的一些软件,可能会采用日文特有的编码方式。

网页提供者规定URL的编码方式

如下图5,6是google和百度搜索引擎在搜索服务中,搜索框中对于汉字的编码采用了UTF-8的编码方式:

在这里插入图片描述
图5
在这里插入图片描述
图6
可以看到,搜索引擎本身规定了汉字在URL中的编码方式,因为百度的表单提交中显示的表明了编码方式为UTF-8。因为表单提交服务是网页供应商的功能,因此其可以决定客户提交数据采用何种编码方式。在客户端用户发送请求的时候,通过调用搜索引擎对应的js脚本进行编码实现,如下图7:
在这里插入图片描述
图7
之前百度的搜索服务也是采用过GB2312方式,现在可能统一了。但是对于国内的网页服务提供商来说,国标的编码方式仍然是一种普遍现象。

浏览器等软件规定URL的编码方式

由于表单提交服务本质是网页提供商提供的,其有解释权,因此可以规定汉字编码方式。当客户端用户并没有提交表单的行为,在初始访问的时候,在地址栏输入了汉字,这个时候浏览器或则和具体的UA会将该汉字进行编码。采用的是UTF-8还是GBK,不同的浏览器可能选择不同,如图8就是IE采用GBK的方式:

在这里插入图片描述
图8
可以看到IE对于汉字,直接采用GBK编码,甚至都没有使用%形式的URL编码。但是对于百度这种比较强大的网页服务提供上来说,他的服务端是能够识别这种方式,并在后续的请求中要求客户端采用%+GBK的方式进行访问,如图9:

在这里插入图片描述

图9
由于UTF-8是国际标准,因此采用UTF-8的居多。比如chrome浏览器,以及采用chrome内核的edge多采用UTF-8方式。由于在汉化的软件中,对于URL的编码也有可能使用GB2312的,例如早期的IE。但是一个URL对于服务端的解析来说一定是固定的,要么采用GBK,要么是UTF-8。如果服务端采用的是UTF-8进行解码,客户端传输的是GBK方式,可能会导致网页无法访问,因此通信双方需要保持编解码一致。

URL编码后都长什么样子

URL编码可以采用公开的组件,例如js中提供的函数,以及Python等语言中的函数,但是要注意这些URL编解码函数存在着一定的差异。有的时候你可能会注意到,实际传输的汉字即不是UTF-8编码方式也不是GBK编码方式,而是一种非常奇怪的方式%u6751,这是什么原因造成的呢?这种情况主要是网页的提供者在对URL进行编码的时候采用的JS函数不同所导致的。例如baidu的js采用的编码函数为encodeURIComponent,如上图7,在js中出了encodeURIComponent进行URL的编码。除此之外js可提供的编码函数还包括encodeURI以及escape函数。因此不同的网页提供者采用的URL编码函数的不同,也就导致可能出现的编码内容的不同。

escape函数作用是返回对应的字符的unicode编号(注意编号和编码的区别,编码有存储的概念,因此存在字节长度),服务端使用unescape解码即可。早期阶段使用该函数完成URL编码的功能,如下图10:
在这里插入图片描述
图10
\u的编码方式目前已经不被提倡使用,因此在如今程序员编写的js中基本很少使用escape。我的猜测是当年escape函数本身的编写人员对于编码并没有理解到位,采用的是unicode编号,同时以四字节对齐来强加存储的概念,这个事情在py2中也是有所体现的。

encodeURI以及encodeURIComponent是当前经常被使用的URL编码函数,这两个函数的区别在于对于特殊字符的编码处理的不太一致,这与前面提到的。对RFC1738的实现,不同的人出现了不同的理解。由js的三个函数可以看出URL编码在实现层面,不同工程师的思路还是有较大的差异。当然关于三个函数的详细区别,可以产看对应的js手册。

前面也曾提到,URL编码最初是为了解决URL中的特殊字符传输问题。但是特殊字符可能会存在任何地方,因此在实际的网络传输中你会发现很多的协议内容为了传输特殊字符,都有采用URL编码的方式进行数据的编解码。因此URL编码只是一种称呼,其应用可以在HTTP的header或者body中,甚至是其他协议中。

本文为CSDN村中少年原创文章,未经允许不得转载,博主链接这里

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

村中少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值