我們知道在 .NET 平臺中有著不同的 URL 編碼方法,但哪個方法才是符合我們需求的方法呢?
我們知道在 .NET 平臺中有著不同的 URL 編碼方法,但哪個方法才是符合我們需求的方法呢?我們接下來會進行分析以評估我們需要的方法。
在這份筆記中包含下列內容:
-
1. 使用案例
當需要在 URL 的 Query String 中傳遞一組 URL 時,我們會透過 URL 編碼將要傳遞的 URL 進行編碼,如下所示:
-
輸入:
http://test.test/~path?Num=123&Space=" "&Symbol=(!*) -
HttpUtility.UrlEncode:
http%3a%2f%2ftest.test%2f%7epath%3fNum%3d123%26Space%3d%22+%22%26Symbol%3d(!*) -
WebUtility.UrlEncode:
http%3A%2F%2Ftest.test%2F%7Epath%3FNum%3D123%26Space%3D%22+%22%26Symbol%3D(!*) -
Uri.EscapeDataString:
http%3A%2F%2Ftest.test%2F~path%3FNum%3D123%26Space%3D%22%20%22%26Symbol%3D%28%21%2A%29
有什麼問題?
-
輸入:
http://test.test/~path?Num=123&Space=" "&Symbol=(!*) -
HttpUtility.UrlEncode:
http%3a%2f%2ftest.test%2f%7epath%3fNum%3d123%26Space%3d%22+%22%26Symbol%3d(!*) -
WebUtility.UrlEncode:
http%3A%2F%2Ftest.test%2F%7Epath%3FNum%3D123%26Space%3D%22+%22%26Symbol%3D(!*) -
Uri.EscapeDataString:
http%3A%2F%2Ftest.test%2F~path%3FNum%3D123%26Space%3D%22%20%22%26Symbol%3D%28%21%2A%29
-
-
2. 什麼是 URL Encoding
首先我們先瞭解什麼是 URL Encoding,其目的、方法與機制。
-
目的與方法:
- 目的:為了在 URI 識別字串序列資源。
- 方法:使用 Percent-Encoding 機制。
-
Percent-Encoding:
- Percent-Encoding 也稱作 URL Encoding,用於表示允許的字元集合外,字元的 8 位元位元組 (8-bit bytes) 序列。
- 由百分號 % 後跟兩個十六進位數字組成。
- 為保持一致性生產器 (producers) 與正規器 (normalizers) 對所有 percent-encodings 使用大寫十六進位數字。
- Percent-encoded = “%”HEXDIG HEXDIG
- 範例:
- US-ASCII 問號「?」
- 二進位:0011 1111
- 十六進位:3F
- Percent-encoded:%3F
-
-
3. 可能遭遇的問題
在瞭解 URL Encoding 後,我們來探討在編碼時可能會遇到的問題以及需要留意的地方。
-
須編碼與不須編碼的字元
首先,我們需要瞭解什麼字元要編碼什麼字元不用編碼,在 RFC 3986 URI: Generic Syntax 請求意見稿中有下列方類:
-
保留字元(在 URI 中合法,有特殊涵義):
:/?#[]@!$&'()*+,;= -
未保留字元(在 URI 中合法):
英文字母、數字與-._~ -
其他字元(在 URI 中非法,必須編碼):
不包含在保留與未保留中的字元
-
-
空格字元的編碼
再來,我們也發現不同方法對於空格字元的編碼結果不逕相同,主要是 RFC 3986 與 HTML 4.01 的定義不同,而這兩種編碼結果都有在使用。
-
RFC 3986 URI: Generic Syntax
Percent-encoded:%20 -
W3C HTML 4.01
application/x-www-form-urlencoded:+
-
-
最大 URL 長度
還有,可能需要注意的是 URL 的最大長度,在不同的網頁伺服器或瀏覽器環境中支援的最大長度也有所不同,以下先列出不分資訊作為參考。
-
RFC 2616 Hypertext Transfer Protocol -- HTTP/1.1:
無指定任何 URL 長度的要求。 -
RFC 3986 URI: Generic Syntax:
URI 網域名稱最長為 255 個字元(因 DNS 限制)。 -
Microsoft Internet Explorer:
URL 最大長度為 2,083 個字元。
-
-
無法被處理的字元
以及,不同的編碼方法 (Method) 所不支援的字元,需要特別留意錯誤處理的機制,如:
-
UTF-16 的 High (Leading) Surrogate 字元,範圍從 U+D800 到 U+DBFF。
-
-
重複編碼
最後,對於 %HEXDIG HEXDIG 序列依照不同的使用情境上可區分為:
- 需要重複編碼
- 不需要重複編碼
-
-
4. .NET 平臺常見的 URL 編碼方法
以下簡單列出一些自己在 .NET 平臺中常見的 URL 編碼方法,我們也會先針對這些方法繼續接下來的探討。
-
.NET Framework 4.7.2
-
System.Web.HttpUtility.UrlEncode(String)
-
System.Net.WebUtility.UrlEncode(String)
-
System.Uri.EscapeDataString(String)
-
System.Uri.EscapeString(String)
-
-
.NET Platform Extensions 2.1
-
System.Text.Encodings.Web.UrlEncoder.Encode(String)
-
-
Flurl
-
Flurl.Url.Encode(String, Boolean)
-
Flurl.Url.EncodeIllegalCharacters(String, Boolean)
-
-
-
5. 分析與比較
接下來我們會對於上述常見的 URL 編碼方法進行分析與比較,讓我們能夠對於不同的使用情境下選擇適當的方法來進行 URL 編碼。
-
透過單元測試進行測試
我們先列出會對 URL 編碼方法的測試案例分類,其中期望的編碼結果皆為大寫的十六進位數字。
項次 測試案例分類 1 RFC 3986 保留字 2 RFC 3986 未保留字 3 空格字元編碼為 %20 4 長度為 65,520 的字串 5 任何的字元皆可接受(無拋出例外) 6 不重複編碼百分號編碼的字串 測試結果如下表所示,其中 ○ 表示全通過、× 表示無通過、△ 表示部分通過並加註通過率。
測試案例分類 HttpUtility
.UrlEncodeWebUtility
.UrlEncodeUri
.Escape
DataStringUri
.Escape
StringUrlEncoder
.EncodeFlurl.Url
.EncodeFlurl.Url
.Encode
Illegal
CharactersRFC 3986 保留字 △ (27.78%) △ (77.78%) ○ × △ (55.56%) ○ × RFC 3986 未保留字 △ (85.71%) △ (85.71%) ○ ○ ○ ○ ○ 空格字元編碼為 %20 × × ○ ○ ○ ○ ○ 長度為 65,520 的字串 ○ ○ × × ○ ○ × 任何的字元皆可接受(無拋出例外) ○ ○ △ (98.44%) △ (98.44%) ○ △ (98.44%) △ (98.44%) 不重複編碼百分號編碼的字串 × × × × × × ○ ※ 期望結果皆為大寫十六進位數字。
-
產生全字元編碼結果以便查找
我們為了瞭解各個方法的編碼結果,也產生一份 CSV 檔案方便需要查找時使用。其中除了列出 URL 編碼方法外,還納入 HTML 與 JavaScript 編碼方法的結果,下面列出是其中一筆結果做為參考。
Input Hexadecimal HttpUtility.UrlEncode HttpUtility.UrlEncodeUnicode HttpUtility.UrlPathEncode WebUtility.UrlEncode < 0x003C %3c %3c < %3C Uri.EscapeDataString Uri.EscapeUriString UrlEncoder.Encode Url.Encode Url.EncodeIllegalCharacters %3C %3C %3C %3C %3C HttpUtility.HtmlAttributeEncode HttpUtility.HtmlEncode HttpUtility.JavaScriptStringEncode WebUtility.HtmlEncode < < \u003c < HtmlEncoder.Encode JavaScriptEncoder.Encode Sanitizer.GetSafeHtmlFragment < \u003c < -
小結
-
非全部保留字有編碼,非全部未保留字無編碼,空格字元非為百分號編碼:
- System.Web.HttpUtility.UrlEncode(String)
- System.Net.WebUtility.UrlEncode(String)
-
非全部保留字有編碼:
- System.Text.Encodings.Web.UrlEncoder.Encode(String)
-
無法處理 High Surrogate 字元,及長度大於 65,519 的字串:
- System.Uri.EscapeDataString(String)
-
僅編碼非法字元且無法處理 High Surrogate 字元,及長度大於 65,519 的字串:
- System.Uri.EscapeString(String)
-
無法處理 High Surrogate 字元:
- Flurl.Url.Encode(String, Boolean)
-
僅編碼非法字元且無法處理 High Surrogate 字元,並不重複編碼 %HEXDIG HEXDIG 的序列字串:
- Flurl.Url.EncodeIllegalCharacters(String, Boolean)
-
-
-
6. 更好的解決方案?
假設上述的方法都不適合使用情境的話,我們有沒有更好的方法?我們也可依照不同的使用情境來自行再開發編碼方法,例如:以 Flurl.Url.Encode 做為主要的編碼方法,若遇到 High Surrogate 字元則改用 UrlEncoder.Encode 編碼。
自行再開發編碼方法的測試結果如下表所示:
測試案例分類 自行開發編碼方法 RFC 3986 保留字 ○ RFC 3986 未保留字 ○ 空格字元編碼為 %20 ○ 長度為 65,520 的字串 ○ 任何的字元皆可接受(無拋出例外) ○ 不重複編碼百分號編碼的字串 × ※ 期望結果皆為大寫十六進位數字。○:全通過、×:無通過、△:部分通過。
-
7. 結論
- 我們瞭解到什麼是 URL Encoding,並知道需要編碼與不需要編碼的字元以及其他需要留意的問題,我們也對常見的方法進行分析比較。
- 最後評估在我們的情境中除了自行開發編碼方法外,會優先選用 Flurl.URL.Encode/Decode,其次選用 System.Uri.EscapeDataString/ UnescapeDataString 進行 URL 的編解碼。
- 完整的程式碼皆推送至個人 GitHub 中 UrlEncodingAnalysis 的儲存庫[14],歡迎有興趣的朋友參考與指教,非常感謝!
-
8. 參考資料
- RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax
- 百分號編碼 - 維基百科,自由的百科全書
- W3C HTML 4.01 - 17.13.4.1 application/x-www-form-urlencoded
- RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1
- Maximum URL length is 2,083 characters in Internet Explorer
- UTF-16 - 維基百科,自由的百科全書
- HttpUtility Class (System.Web) | Microsoft Docs
- WebUtility Class (System.Net) | Microsoft Docs
- Uri Class (System) | Microsoft Docs
- UrlEncoder Class (System.Text.Encodings.Web) | Microsoft Docs
- Fluent URL Building – Flurl
- Better URL encoding/decoding · Issue #262 · tmenier/Flurl · GitHub
- asp.net - Server.UrlEncode vs. HttpUtility.UrlEncode - Stack Overflow
- Zhi-Wei/UrlEncodingAnalysis - GitHub
本著作由Zhi-Wei製作,以創用CC 姓名標示-非商業性-相同方式分享 4.0 國際 授權條款釋出。