本文主要針對URI編解碼的相關(guān)問題做了介紹蠢古,對Url編碼中哪些字符需要編碼奴曙、為什么需要編碼做了詳細(xì)的說明,并對比分析了Javascript 中和編解碼相關(guān)的幾對函數(shù)escape / unescape,encodeURI / decodeURI和encodeURIComponent / decodeURIComponent
預(yù)備知識
URI是統(tǒng)一資源標(biāo)識的意思草讶,通常我們所說的Url只是URI的一種洽糟。典型Url的格式如上面所示。下面提到的Url編碼,實際上應(yīng)該指的是URI編碼坤溃。
為什么需要Url編碼
通常如果一樣?xùn)|西需要編碼拍霜,說明這樣?xùn)|西并不適合傳輸。原因多種多樣薪介,如Size過大祠饺,包含隱私數(shù)據(jù),對于Url來說汁政,之所以要進(jìn)行編碼道偷,是因為Url中有些字符會引起歧義。
例如Url參數(shù)字符串中使用key=value鍵值對這樣的形式來傳參记劈,鍵值對之間以&符號分隔勺鸦,如/s?q=abc& ie=utf-8。如果你的value字符串中包含了=或者&目木,那么勢必會造成接收Url的服務(wù)器解析錯誤换途,因此必須將引起歧義的&和= 符號進(jìn)行轉(zhuǎn)義,也就是對其進(jìn)行編碼嘶窄。
又如怀跛,Url的編碼格式采用的是ASCII碼距贷,而不是Unicode柄冲,這也就是說你不能在Url中包含任何非ASCII字符,例如中文忠蝗。否則如果客戶端瀏覽器和服務(wù)端瀏覽器支持的字符集不同的情況下现横,中文可能會造成問題。
Url編碼的原則就是使用安全的字符(沒有特殊用途或者特殊意義的可打印字符)去表示那些不安全的字符阁最。
哪些字符需要編碼
RFC3986文檔規(guī)定戒祠,Url中只允許包含英文字母(a-zA-Z)、數(shù)字(0-9)速种、-_.~4個特殊字符以及所有保留字符姜盈。
RFC3986文檔對Url的編解碼問題做出了詳細(xì)的建議,指出了哪些字符需要被編碼才不會引起Url語義的轉(zhuǎn)變配阵,以及對為什么這些字符需要編碼做出了相應(yīng)的解釋馏颂。
US-ASCII字符集中沒有對應(yīng)的可打印字符
Url中只允許使用可打印字符。US-ASCII碼中的10-7F字節(jié)全都表示控制字符棋傍,這些字符都不能直接出現(xiàn)在Url中救拉。同時,對于80-FF字節(jié)(ISO-8859-1)瘫拣,由于已經(jīng)超出了US-ACII定義的字節(jié)范圍亿絮,因此也不可以放在Url中。
保留字符
Url可以劃分成若干個組件,協(xié)議派昧、主機(jī)黔姜、路徑等。有一些字符(:/?#[]@)是用作分隔不同組件的斗锭。例如:冒號用于分隔協(xié)議和主機(jī)地淀,/用于分隔 主機(jī)和路徑,?用于分隔路徑和查詢參數(shù)岖是,等等帮毁。還有一些字符(!$&'()*+,;=)用于在每個組件中起到分隔作用的,如=用于表示查詢參數(shù)中 的鍵值對豺撑,&符號用于分隔查詢多個鍵值對烈疚。當(dāng)組件中的普通數(shù)據(jù)包含這些特殊字符時,需要對其進(jìn)行編碼聪轿。
!|* ' ( ) ; : @ & = + $ , / ? # [ ] **
不安全字符
還有一些字符爷肝,當(dāng)他們直接放在Url中的時候,可能會引起解析程序的歧義陆错。這些字符被視為不安全字符灯抛,原因有很多。
動詞 | 含義 |
---|---|
空格 | Url在傳輸?shù)倪^程音瓷,或者用戶在排版的過程对嚼,或者文本處理程序在處理Url的過程,都有可能引入無關(guān)緊要的空格绳慎,或者將那些有意義的空格給去掉 |
引號以及<> | 引號和尖括號通常用于在普通文本中起到分隔Url的作用 |
# | 通常用于表示書簽或者錨點 |
% | 百分號本身用作對不安全字符進(jìn)行編碼時使用的特殊字符纵竖,因此本身需要編碼 |
{}\|^[]`~ | 某一些網(wǎng)關(guān)或者傳輸代理會篡改這些字符 |
需要注意的是,對于Url中的合法字符杏愤,編碼和不編碼是等價的靡砌,但是對于上面提到的 這些字符,如果不經(jīng)過編碼珊楼,那么它們有可能會造成Url語義的不同通殃。因此對于Url而言,只有普通英文字符和數(shù)字厕宗,特殊字符$-_.+!*'()還有保留 字符画舌,才能出現(xiàn)在未經(jīng)編碼的Url之中。其他字符均需要經(jīng)過編碼之后才能出現(xiàn)在Url中媳瞪。
但是由于歷史原因骗炉,目前尚存在一些不標(biāo)準(zhǔn)的編碼實現(xiàn)。例如對于符號蛇受,雖然RFC3986文檔規(guī)定句葵,對于波浪符號,不需要進(jìn)行Url編碼,但是還是有很多老的網(wǎng)關(guān)或者傳輸代理會
Javascript中的escape,encodeURI和encodeURIComponent的區(qū)別
Javascript中提供了3對函數(shù)用來對Url編碼以得到合法的Url乍丈,它們分別是escape / unescape,encodeURI / decodeURI和encodeURIComponent / decodeURIComponent剂碴。由于解碼和編碼的過程是可逆的,因此這里只解釋編碼的過程轻专。
這三個編碼的函數(shù)——escape忆矛,encodeURI,encodeURIComponent——都是用于將不安全不合法的Url字符轉(zhuǎn)換為合法的Url字符表示请垛,它們有以下幾個不同點
安全字符不同
下面的表格列出了這三個函數(shù)的安全字符(即函數(shù)不會對這些字符進(jìn)行編碼)
安全字符 | |
---|---|
escape(69個) | */@+-._0-9a-zA-Z |
encodeURI(82個) | !#$&'()*+,/:;=?@-._~0-9a-zA-Z |
encodeURIComponent(71個) | !'()*-._~0-9a-zA-Z |
兼容性不同
escape函數(shù)是從Javascript1.0的時候就存在了催训,其他兩個函數(shù)是在Javascript1.5才引入的。但是由于 Javascript1.5已經(jīng)非常普及了宗收,所以實際上使用encodeURI和encodeURIComponent并不會有什么兼容性問題漫拭。
對Unicode字符的編碼方式不同
這三個函數(shù)對于ASCII字符的編碼方式相同,均是使用百分號+兩位十六進(jìn)制字符來表示混稽。但是對于Unicode字符采驻,escape的編碼方式是%uxxxx,其中的xxxx是用來表示unicode字符的4位十六進(jìn)制字符匈勋。這種方式已經(jīng)被W3C廢棄了礼旅。但是在ECMA-262標(biāo)準(zhǔn)中仍然保留著escape的這種編碼語法。encodeURI和encodeURIComponent則使用UTF-8對非ASCII字符進(jìn)行編碼洽洁,然后再進(jìn)行百分號編碼痘系。這是RFC推薦的。因此建議盡可能的使用這兩個函數(shù)替代escape進(jìn)行編碼诡挂。
適用場合不同
encodeURI被用作對一個完整的URI進(jìn)行編碼碎浇,而encodeURIComponent被用作對URI的一個組件進(jìn)行編碼临谱。
從上面提到的安全字符范圍表格來看璃俗,我們會發(fā)現(xiàn),encodeURIComponent編碼的字符范圍要比encodeURI的大悉默。我們上面提到 過城豁,保留字符一般是用來分隔URI組件(一個URI可以被切割成多個組件,參考預(yù)備知識一節(jié))或者子組件(如URI中查詢參數(shù)的分隔符)抄课,如:號用于分隔 scheme和主機(jī)唱星,?號用于分隔主機(jī)和路徑。由于encodeURI操縱的對象是一個完整的的URI跟磨,這些字符在URI中本來就有特殊用途间聊,因此這些保 留字符不會被encodeURI編碼,否則意義就變了抵拘。
組件內(nèi)部有自己的數(shù)據(jù)表示格式哎榴,但是這些數(shù)據(jù)內(nèi)部不能包含有分隔組件的保留字符,否則就會導(dǎo)致整個URI中組件的分隔混亂。因此對于單個組件使用encodeURIComponent尚蝌,需要編碼的字符就更多了迎变。