原文地址:http://www.imkevinyang.com/2009/08/詳解javascript中的url編解碼.html
摘要
- URI(統一資源標識)編解碼
- 為什么需要編碼
- 哪些需要編碼
- 如何編碼
預備知識
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/ \________/\_________/ \__/
| | | | |
scheme authority path query fragment
上面為典型的URL的格式。由於URL屬於URI的一種,所以下面提到的URL編碼,實際上應該指的是URI編碼。
為什么需要URL編碼?
通常如果一樣東西需要編碼,說明這樣東西並不適合傳輸。原因多種多樣,如:內容Size過大、包含隱私數據等。
URL的編碼原因是因為URL中有些字符會引起歧義。例如:URL參數字符串中使用鍵值對(key=value)的方式來傳參,鍵值對之間以&符號分隔,如"/s?q=abc& ie=utf-8"。如果value字符串中包含了'='或者'&',那么勢必會造成接收URL的服務器解析錯誤,因此必須將引起歧義的'='或者'&'符號進行轉義,也就是對其進行編碼。
又如,URL的編碼格式采用的是ASCII碼,而不是Unicode,所以你不能在URL中包含任何非ASCII字符,例如中文。否則如果客戶端瀏覽器和服務端瀏覽器支持的字符集不同的情況下,可能會造成問題。
URL編碼的原則就是使用安全的字符(沒有特殊用途或者特殊意義的可打印字符)去表示那些不安全的字符。
哪些字符需要編碼?
RFC3986文檔規定,URL中只允許包含英文字母(a-zA-Z)、數字(0-9)、4個特殊字符(- _ . ~)以及所有保留字符。
RFC3986文檔對URL的編解碼問題做出了詳細的建議,指出了哪些字符需要被編碼才不會引起URL語義的轉變,以及對為什么這些字符需要編碼做出了相應的解釋。
URL中只允許使用可打印字符。US-ASCII碼中的10-7F字節全都表示控制字符,這些字符都不能直接出現在URL中。同時,對於80-FF字節(ISO-8859-1),由於已經超出了US-ACII定義的字節范圍,因此也不可以放在URL中。
保留字符。URL可以划分成若干個組件:協議、主機、路徑等。有一些字符(:/?#[]@)是用作分隔不同組件的。例如:':'用於分隔協議和主機,'/'用於分隔主機和路徑,'?'用於分隔路徑和查詢參數,等等。還有一些字符(!$&'()*+,;=)用於在每個組件中起到分隔作用的,如'='用於表示查詢參數中 的鍵值對,'&'符號用於分隔查詢多個鍵值對。當組件中的普通數據包含這些特殊字符時,需要對其進行編碼。RFC3986中指定了以下字符為保留字符:
| ! | * | ' | ( | ) | ; | : | @ | & | = | + | $ | , | / | ? | # | [ | ] |
不安全字符。還有一些字符,當他們直接放在URL中的時候,可能會引起解析程序的歧義。這些字符被視為不安全字符,原因有很多。
| 空格 |
在傳輸、用戶排版、文本處理程序處理URL的過程中都有可能引入無關緊要的空格或者去掉了有意義的空格 |
| 引號以及<> | 引號和尖括號通常用於在普通文本中起到分隔Url的作用 |
| # | 通常用於表示書簽或者錨點 |
| % | 百分號本身用作對不安全字符進行編碼時使用的特殊字符,因此本身需要編碼 |
| {}|\^[]`~ | 某一些網關或者傳輸代理會篡改這些字符 |
注意:對於URL中的合法字符,編碼和不編碼是等價的,但對於上面提到的這些字符如果不經過編碼,那么它們有可能會造成URL語義的不同。因此對於URL而言,只有普通英文字符和數字,特殊字符$-_.+!*'()還有保留字符,才能出現在未經編碼的URL之中。其他字符均需要經過編碼。
但是由於歷史原因,目前尚存在一些不標准的編碼實現。例如對於'~'符號,雖然RFC3986文檔規定,對於波浪符號~,不需要進行URL編碼,但是還是有很多老的網關或者傳輸代理會。
如何對Url中的非法字符進行編碼?
URL編碼通常也被稱為百分號編碼(Url Encoding,also known as percent-encoding),是因為它的編碼方式非常簡單,使用%百分號加上兩位的字符(十六進制0~F)。URL編碼默認使用的字符集是US-ASCII。例如 a 在US-ASCII碼中對應的字節是0x61,那么URL編碼之后得到的就 是%61,我們在地址欄上輸入http://g.cn/search?q=%61%62%63,實際上就等同於在google上搜索abc了。又如@符號在ASCII字符集中對應的字節為0x40,經過URL編碼之后得到的是%40。
常見字符的URL編碼列表:
| ! | * | " | ' | ( | ) | ; | : | @ | & |
%21 |
%2A |
%22 |
%27 |
%28 |
%29 |
%3B |
%3A |
%40 |
%26 |
| = | + | $ | , | / | ? | % | # | [ | ] |
%3D |
%2B |
%24 |
%2C |
%2F |
%3F |
%25 |
%23 |
%5B |
%5D |
對於非ASCII字符,需要使用ASCII字符集的超集進行編碼得到相應的字節,然后對每個字節執行百分號編碼。 對於Unicode字符,RFC文檔建議使用utf-8對其進行編碼得到相應的字節,然后對每個字節執行百分號編碼。如:"中文"使用UTF-8字符集得到的字節為0xE4 0xB8 0xAD 0xE6 0x96 0x87,經過URL編碼之后得到"%E4%B8%AD%E6%96%87"。
如果某個字節對應着ASCII字符集中的某個非保留字符,則此字節無需使用百分號表示。 例如:"URL編碼",使用UTF-8編碼得到的字節是0x55 0x72 0x6C 0xE7 0xBC 0x96 0xE7 0xA0 0x81,由於前三個字節對應着ASCII中的非保留字符"URL",因此這三個字節可以用非保留字符"URL"表示。最終的URL編碼可以簡化成"URL%E7%BC%96%E7%A0%81",當然,如果你用"%55%72%6C%E7%BC%96%E7%A0%81"也是可以的。
由於歷史的原因,有一些URL編碼實現並不完全遵循這樣的原則。
其他和URL編碼相關的問題
對包含中文的URL的處理問題,不同瀏覽器有不同的表現。例如:對於IE,如果你勾選了高級設置"總是以UTF-8發送URL",那么URL路徑部分的中文會使用UTF-8進行URL編碼之后發送給服務端,而查詢參數中的中文部分使用系統默認字符集進行URL編碼。為了保證最大的互操作性,建議所有放到URL中的組件全部顯式指定某個字符集進行URL編碼,而不依賴於瀏覽器的默認實現。
另外,很多HTTP監視工具或者瀏覽器地址欄等在顯示URL的時候會自動將URL進行一次解碼(使用UTF-8字符集),這就是為什么當你在 Firefox中訪問Google搜索中文的時候,地址欄顯示的URL包含中文的緣故。但實際上發送給服務端的原始URL還是經過編碼的。你可以在地址欄上使用Javascript訪問location.href就可以看出來了。在研究URL編解碼的時候千萬別被這些假象給迷惑了。
