混亂的 URLEncode

因為噗浪朋友的一則訊息,讓我興起了想要整理一些關於 URLEncode 的問題,整理一下才發現每個語言都有不同的函式來處理關於 URLEncode 的問題。

先說為什麼需要作 URLEncode,RFC 3986規範了哪些字元是作為保留字(如:!@/?等),如果URL中使用到了這些保留字,就必須將它編碼為「%HEXHEX」的形式,舉例來說,「空白字元」的 ASCII code 是32,所以會被編碼為 %20,而其它 non-ASCII 字元(如:中文字)則以 UTF-8 字元編碼後的位元組來編碼成 %HEXHEX 的形式。

如果有一個字串是:「This is my **書本**」,根據 RFC 3986 的定義,作完 URL encode 之後應該會變成「This%20is%20my%20%2A%2A%E6%9B%B8%E6%9C%AC%2A%2A」。

那什麼時候會需要對文字作 URL encode 呢?比方說,Facebook 提供了一個分享網頁到個人塗鴨牆(Wall)的作法,那就是將欲分享的網頁URL,比方說是 http://www.example.com/,只要將它作為參數(u傳給http://www.facebook.com/share.php就可以了,但你千萬不能寫成:

http://www.facebook.com/share.php?u=http://www.example.com/

,而是必須將它作 URL encode 之後,組合成:

http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.example.com%2F

這樣才是正確的。

若是你在瀏覽器的網址列直接輸入前者也會正確,那是因為瀏覽器會幫你作了 url encode

不過,在 RFC 3986 之前,HTTP 也有為 GET/POST 在傳遞參數時的 url encode 方式作定義,基本上也是 encode 成 %HEXHEX 的形式,保留字跟 RFC 3986 的有部份出入,不過就沒有限定一定是 UTF-8 的文字編碼了,而且還有一個重點--「空白字元」會被編碼成 + 而不是 %20。所以「This is a book」就會被編碼成「This+is+a+book」。

常用語言的函式庫

因為有這樣的差異,所以開發人員在使用函式庫的時候常常會搞混什麼時候該用/不該用什麼函式,以 PHP 來說就分為兩組函式:

  • urlencode / urldecode

    簡單地說就是以 HTTP 所使用的 application/x-www-form-urlencoded 的編碼規則,也就是會將空白字元編碼成 + 而不是%20

  • rawurlencode / rawurldecode

    按照 RFC 3986 所定義的方式來作編碼。

Python 版本的話就是:

  • urllib.urlencode / urllib.urldecode

    會把空白字元編碼成 +

  • 目前要使用 Python 3 以後的 urllib.parse.urlencode 才會按照 RFC 3986 的方式來作編碼,若是 2.x 的版本就要另外處理或是找 3rd-party 資料庫來做。

JavaScript 的 encodeURI 或是 encodeURIComponent (兩者僅相差一些保留字是否要作編碼,如 #)目前則是都使用 RFC 3986 的方式來作編碼,所以要作 application/x-www-form-urlencoded 的編碼時(AJAX POST),就要自己把 %20 替換成 + (jQuery 目前的程式碼就是這樣做的)

Java 的 java.net.URLEncoder.encode 這個 method 也是編碼成 application/x-www-form-urlencoded 的方式,如果要遵照 RFC 3986 的定義,則可以自行再把 + 替換成 %20 即可。

  • yi-chun

    非常棒! 我前陣子也是因為這件事情再煩惱(中文傳輸資料問題)! 也是用URLEncode解決了中文問題!!good post

  • Indiana

    整理得很清楚、詳細,多謝!

  • tom

    請問, we’ll meet網站 現在還有在維護更新嗎?謝謝!!

  • 支持一下我的网站http://www.q666.net/

  • kakashi

    原來是這樣, 終於解惑了!

  • sean

    還有一種 %uxxxx 表示,例如「中文」是 %u4E2D%u6587

    • 北極熊貓

      %uHHHH的表示法並非標準, 早期IIS好像有支援, 現在有支援的Web Server 已經愈來愈少了.
      在URL encode 之前其實還有Charset 的轉換. 版主提的是UTF-8之下的URL encode,
      如果Charset是big5或GB. 那encoding 後的結果又不一樣.
      最後. sean 提到的是UTF-16下的URL encode, 不過UTF-16是base 在word size為2byte的情況, 如bstr .所以解開後無法存入Ansi-compatiable 的字串, 如char*
      所以大多數的系統還是使用Ansi-compatiable 的UTF-8 來做URL encode

  • 學習了

  • Pingback: URL Encode 的迷思 | Dreaming..*()