fbpx

淺談 HTTP URL 規範 - 善用 JavaScript encodeURIComponent() 與 PHP rawurlencode() 來進行 URL 編碼

惱人的 URL Encode/Decode

我們常在開發 Web 時,會發生連結失效 (404 Page not found.)、特別的參數值傳遞不正確、Query String 解譯錯誤等等問題或 Bug。追究之後常常都是忽略了 URL 編碼或者用了不正確的編碼所造成,看來我們需要多多認識 URL 的特性,順便養成「對 URL 敏感」的習慣。

URI 規範的歷史淵源

先來看看 URI 的歷史。最早在 1994 年 12 月,RFC 1738 起頭定義了 URI 最初的規範。經過了幾年的混亂與磨練,一直到了 1998 年 8 月才有了比較廣泛的標準,那就是 RFC 2396。而我們最常接觸的 RFC 2616 (也就是 HTTP 1.1) 在處理 URI 也是遵循 RFC 2396 標準,我們可以在規範看見以下描述:

3.2.3 URI Comparison
Characters other than those in the "reserved" and "unsafe" sets (see RFC 2396 [42]) are equivalent to their ""%" HEX HEX" encoding.

但是後來隨著環境的演進,直到 2005 年 1 月所發佈的 RFC 3986 最終成為目前主流所實作的規範。因此以目前來說,採用 RFC 3986 是比較明智的選擇。

PHP urlencode() 與 rawurlencode() 的差異

先來測試一下:

<?php

$str='1+2&b and c';

echo urlencode($str);
// show: 1%2B2%26b+and+c

echo rawurlencode($str);
// show: 1%2B2%26b%20and%20c

發現了嗎?兩者在於對空白 (space) 的處理有不同的邏輯,根據官方文件描述 PHP 中的 urlencode() function 是遵循 application/x-www-form-urlencoded (就是我們常說的 Query String) 編碼規範,因此會把 space 空白變成 + 而不是  %20,如下:

urlencode differs from RFC 1738 by encoding spaces as + instead of %20

然而 PHP 中的 rawurlencode() function 是遵循 RFC 3986 進行實作,如下:

Encodes the given string according to » RFC 3986.

由此看來使用 rawurlencode() 才是最推薦的方法,測試如下:

JavaScript 中的 encodeURIComponent() 與 encodeURI() 的差異

JavaScript 測試如下:

<script type="text/javascript">
	document.write(encodeURIComponent('1+2&b and c'));
	// show: 1%2B2%26b%20and%20c
</script>

<script type="text/javascript">
	document.write(encodeURI('1+2&b and c'));
	// show: 1+2&b%20and%20c
</script>

在 JavaScript 中,encodeURIComponent() 與 encodeURI() 差別在保留字 (不進行編碼的字元) 的定義不同:

encodeURI() 的保留字為 ~!@#$&*()=:/,;?+'

encodeURIComponent() 的保留字為 ~!*()'

此外 JavaScript 中的 encodeURIComponent() 也是遵循 RFC 3986 編碼,因此比較推薦採用這個函式。

討論

其實最混亂的就是 + 與 %20 的問題,在 JavaScript 中所提供的 encodeURI() 並不是遵循 application/x-www-form-urlencoded 編碼規範,如果要在 JavaScript 環境下自行實作 application/x-www-form-urlencoded 編碼就必須先使用 encodeURIComponent() 轉換,然後再另外將 %20 取代為 + 即可,如此就達到跟 PHP urlencode() 一樣的效果。

此外,我們在 Web 開發的過程中輸出 HTML 連結,雖然大多數瀏覽器都會很友善的自動對網址進行轉換再送出 HTTP Request 連線,但未了避免例外情況,我們還是遵循 RFC 3986 來處理 URL 比較正確,相容性也比較高。

參考資料

發佈留言