爲什麼要進行URL編碼

轉載自http://www.cnblogs.com/jerrysion/p/5522673.html


HTTP協議中參數組件的傳輸是“key=value”鍵值對的形式,如果要傳輸多個參數就需要用“&”符號對鍵值對進行分隔。例如?name1=value1&name2=$value2,這樣在服務器收到這種字符串的時候,會用“&”分隔出每一個參數,然後再用“=”來分隔出參數值。

針對“name1=value1&name2=value2”我們來說一下客戶端到服務器端的概念上解析過程:
上述字符串在計算機中用ASCII碼(16進制)表示爲:
6E616D6531 3D 76616C756531 26 6E616D6532 3D 76616C756532

ASCII碼 含義
6E616D6531 name1
3D =
76616C756531 value1
26 &
6E616D6532 name2
3D =
76616C756532 value2

服務器端在接收到該數據後就可以遍歷該字節流,首先一個字節一個字節的讀取,當讀到3D這個字節的時候,服務器端就知道前面讀到的字節串表示一個key,繼續讀取,如果遇到了26,表示從剛纔讀到的3D到26字節之間的字節串是上一個key的value,按照此方法就可以解析出客戶端傳過來的參數。

現在又這樣一個問題:如果我的參數值中就包含=或者&這樣的特殊子字符的時候,該怎麼辦。比如說“name1=value1”,其中value1的值是“va&lu=e1”,那麼在傳輸過程中就會變成“name1=va&lu=e1”。用戶傳輸的本意是隻有一個鍵值對,但是服務器端會解析成兩個鍵值對,這樣就自然的產生了歧義。


如何解決上述問題帶來的歧義呢?解決之法就是對URL進行編碼

URL編碼只是簡單的在特殊字符的各個字節(16進制)前加上”%”即可。例如,我們對上述會產生歧義的字符進行編碼後的結果:name1=va%26lu%3D,這樣服務器會把緊跟在”%”後的字節當成普通的字節,不會把它當成各個參數或鍵值對的分隔符。

另外一個問題是,爲什麼要用ASCII碼傳輸,可不可以用別的編碼?
當然可以用別的編碼,你可以自己開發一套編碼然後自己進行解析。就像大部分國家都有自己的語言一樣。但是國家之間要怎麼進行交流呢,用英語吧,英語的使用範圍最廣。


通常如果一樣的東西需要編碼,就說明這樣的東西並不適合傳輸。至於原因有多種多樣,size過大,包含隱私數據等等。對於URL來說,之所有要進行編碼,是因爲URL中有些字符會引起歧義。
例如,URL參數字符串中如果包含”&”或者”%”勢必會造成服務器解析錯誤,所以需要對其進行編碼。
又如,URL的編碼格式採用的是ASCII碼而不是Unicode,這也就是說你不能在URL中包含任何非ASCII字符,比如中文。否則如果客戶端瀏覽器和服務器端瀏覽器支持的字符集不同的情況下,中文可能會造成問題。

URL編碼的原則就是使用安全的字符(沒有特殊用途或者特殊意義的可打印字符)去表示那些不安全的字符

哪些字符需要編碼
RFC3986文檔規定,URL中只允許包含英文字母(a-zA-Z)、數字(0-9)、- _ . ~4個特殊字符以及所有的保留字符。RFC3986文檔對URL的編碼解碼問題做出了詳細的建議,指出了哪些字符需要被編碼纔不會引起URL語義的轉變,以及對爲什麼這些字符需要編碼做出了相應的解釋。

US-ASCII字符集中沒有對應的可打印字符:URL中只允許使用可打印的字符。US-ASCII碼中的10-7F字節全都表示控制字符,這些字符不能直接出現在URL中。同時對於80-FF字節,由於已經超出了ASCII碼定義字符的範圍,因此也不能放在URL中。

保留字符:RUL可以劃分爲幹了組件,協議、主機、路徑等。有一些字符(: / ? # [ ] @)是用作分隔不同組件的。例如:冒號用於分隔協議和主機組件,斜槓用於分隔主機和路徑,問號用於分隔路徑和查詢參數,等等。還有一些字符(! $ & * + , ; =)用於在每個組件中起到分隔作用,如等號用於表示查詢參數中的鍵值對,&符號用於分隔查詢多個鍵值對。當組件中的普通數據包含這些特殊字符時,需要對其進行編碼。
RFC3986中指定了以下字符爲保留字符: ! * ’ ( ) ; : @ & = + $ , / ? # [ ]

不安全字符:還有一些字符,當他們直接放在URL中的時候,可能會引起解析程序的歧義。這些字符被視爲不安全的字符,原因有很多。


  • 空格:URL在傳輸的過程,或者用戶在排版的過程中,或者文本處理程序在處理URL的過程,都有可能引入無關緊要的空格,或者將那些有意義的空格給去掉。
  • 引號 以及 <>:引號和尖括號通常用於在普通文本中起到分隔URL的作用。
  • #:通常用於表示書籤或者錨點。
  • %:百分號本身用作對不安全的字符進行編碼是使用的特殊字符,因此本身需要編碼。
  • { } | \ ^ [ ] ’ ~:某一些網關或者傳輸代理會篡改這些字符

需要注意的是,對於URL中的合法字符,編碼和不編碼是等價的,但是對於上邊提到的這些字符,如果不經過編碼,那麼它們可能會造成URL語義的不同。因此對於URL而言,只有普通英文字符和數字,特殊字符$ - _ . + ! * ’ ( )還有保留字符,才能出現在未經編碼的Url中,其他字符均需要編碼之後才能出現在URL中。

但是由於歷史原因,目前尚存在一些不標準的編碼實現,例如對於”~”符號,雖然RFC3986文檔規定,對於波浪號~不需要進行URL編碼,但是還是有很多老的網關或者傳輸代理會進行編碼。

如何對URL中的非法字符進行編碼

URL編碼通常也被稱爲百分號編碼,是因爲它的編碼方式非常簡單,使用%加上兩位字符———[0-9A-F]———代表一個字節的十六進制的形式。URL編碼默認使用的字符集是US-ASCII碼,例如a在US-ASCII碼中對應的字節值是0x61,那麼URL編碼之後得到的就是%61,我們在地址欄中輸入http://g.cn/search?q=%61%62%63,實際上就等於在google中搜索abc。又如@符號在ASCII字符集中對應的字節爲0x40,經過URL編碼之後得到的就是%40.

對於非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編碼實現並不完全遵循這樣的原則
JS中提供3個函數對URL進行編碼和解碼 escape/unescape,encodeURI
/decodeURI,encodeURIComponent/decodeURIComponent.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章