url之所以要编码,不外乎几个原因,比如:url中某些字符会引起歧义、url参数中含有隐私数据、url中参数中含有中文(url编码格式采用ascii而非unicode)等。
一、参数字符串
url参数字符串中使用key=value键值对这样的形式来传递参数,键值对之间以&符号分隔,比如:/ex?i=mm_1_2_3&cb=jsonp_callback。如果value字符串中包含了=或者&,那么就会造成接收url的服务器解析错误,因此,必须对会引起歧义的符号=和&进行转义,即对其进行编码。
典型的url格式如下:
二、编解码字符
rfc3986规定url中只允许包含英文字母(a-za-z)、数字(0-9)、4个特殊字符(-、_、.、~)以及所有保留字符(比如:/、?、[]、@等)。rfc3986对url编码问题做了详细建议,指出了哪些字符需要编码才不会引起url语义歧义,以及对这些字符编码的原因。
url可以划分为若干组件,比如:协议、主机、路径等,保留字符中有些就是用作分隔不同组件的,比如:冒号用于分隔协议和主机,/用于分隔主机和路径,?用于分隔路径和查询参数等,还有一些字符用于每个组件内起到分隔作用,比如:=用于分隔查询参数中的key=value,&用于分隔查询参数中的多个键值对等。当组件中的普通数据包含这些特殊字符使,需要对其进行编码。
rfc3986中指定保留字符包括:!、*、'、()、;、:、@、&、=、+、$、,、/、?、#、[],此外,还有一些字符也不能直接出现在url中而需要编码,比如:空格、引号、<>、#、%(用作编码时使用的特殊字符)、{}、|、、^、[]、`、~等。
总之,url中如果出现非字母和数字字符时,就去查一下rfc3986文档以确认是否需要编码,而不是靠臆测。
三、编解码方式
url编码(url encoding)也称为百分号编码(percent-encoding),即其使用百分号(%)加上两位字符(0123456789abcdef)来代表一个字节的十进制形式(即被编码字符),其实就是用%+两个十六进制字符来表示一个字符。url编码默认使用的字符集是ascii,例如:字符a在ascii码中对应的十六进制是0x61(十进制是97),那么url编码后得到的就是%61。实际中,字符a是不需要编码的。
对于非ascii字符,需要使用ascii字符集的超集进行编码得到相应的字节,然后对每个字节执行百分号编码。
对于unicode字符,rfc文档建议使用utf-8对其进行编码得到相应的字节,然后对每个字节执行百分号编码。例如:“中文”使用utf-8字符集得到的字节为0xe4、0xb8、0xad、0xe6、0x96、0x87,经过url编码之后得到%e4、%b8、%ad、%e6、%96、%87。
各种语言都需要编写自己对应的编码和解码函数,c/c++的可以参见这里。此处,以javascript为例说明之,javascript中提供了3对函数用于对url进行编码和解码,分别是escape/unescape、encodeuricomponent/decodeuricomponent、encodeuri/decodeuri。
escape编码基本已经废弃,后两者使用utf-8对非ascii字符进行编码,然后再进行百分号编码,这是rfc推荐使用的方式。
encodeuricomponent和encodeuri区别在于适用场合不同:encodeuricomponent被用作对uri的一个组件进行编码,而encodeuri被用作对一个完整的uri进行编码。
二者编码字符范围不一样,即前者比后者范围大。附上三者无需编解码的字符范围作为结束: