UrlEncode() 與空白變加號問題
4 | 16,173 |
在 ASP.NET Core 遇到轉換網址中文及特殊字元的需求,由於 .NET Core 不適合再用 System.Web.HttpUtility,爬文查到有個替代品 - System.Net.WebUtility.UrlEncode,開開心心上路卻踢到鐵板。
問題出在 System.Net.WebUtility.UrlEncode() 會將空白字元轉成加號,而 IIS7+ 預設禁止在網址使用加號,否則必須修改 allowDoubleEscaping 設定,但如此將增加風險。(延伸參考:IIS 7+禁止URL路徑使用加號代表空白 - 黑暗執行緒)
既然遇到,順手蒐集整理資料,看看 .NET 裡 UrlEncode 該怎麼寫才好。
- 如果是寫 .NET Framework,最普遍的做法是用 System.Web.HttpUtility.UrlEncode(),而官方文件提到 UrlEncode() 會將空白換成+,用於傳送表單時的 application/x-www-form-urlencoded 編碼 OK,但當成 URL 網址就有問題,文件建議改用 UrlPathEncode(),但 UrlPathEncode() 方法又說「這方法過時了別用,請改用 UrlEncode()!」(Do not use; intended only for browser compatibility. Use UrlEncode(String).) 這... (第一次看到兩個 API 互踢皮球,很妙)
- 在 ASP.NET Core 不建議再用 System.Web,System.Net.WebUtility.UrlEncode() 可為替代,但它跟 HttpUtility.UrlEncode() 一樣,空白會被轉成 +,而且沒有 UrlPathEncode() 這種版本。
- .NET Core 還有個套件 System.Text.Encodings.Web,提供 HtmlEncoder、UrlEncoder、JavaScriptEncoder 等編碼功能,可透過 System.Text.Encodings.Web.UrlEncoder.Default.Encode("...") 轉換,空白會轉成 %20,可用。但缺點是它限定 .NET Core 平台,且只為了一個函式要加裝套件似乎有點搞剛。
- 最後,我想起 System.Uri 有個 EscapeDataString() 也可以用。System.Uri 本身是處理 URL 的專家,轉換結果用在 URL 肯定沒問題。 加上 System.Uri 為 .NET 核心型別,不管 .NET Framework 或 .NET Core 均可直接引用,評估之後是最方便的解法。
註:Uri.EscapeDataString() 之外還有個 Uri.EscapeUriString(),二者差不多就是 JavaScript encodeURIComponent 與 encodeURI 的區別。(EscapeDataString = encodeURIComponent, EscapeUriString = encodeURI,詳情可參考這篇:JavaScript 網址編碼函式 escape encodeURI encodeURIComponent 的不同 @ Vexed's Blog )
【結論】
未來要將中文跟特殊字元做 UrlEncode 編碼串進 URL,JavaScript 端用 encodeURICompoent,.NET 端則一律改用 Uri.EscapeDataString() 就對了。
Tips of how to encode space in URL correctly, System.Uri.EscapeDataString() is the best solution for both .NET Framework and .NET Core.
Comments
# by 艾里克斯
URL Encode 真是門學問, 只有不斷被雷的人才能體會呀~
# by fmnijk
寫得太好了!!
# by Irene
請問.NET Core 不適合再用 System.Web.HttpUtility...是只有 UrlEncode() 這個Method不適用,還是整個 System.Web.HttpUtility均不適用
# by Jeffrey
to Irene, System.Web.dll 是 .NET Framework 時代的產物,ASP.NET Core 架構重新改寫過已不再靠它運作,雖然有移植一些 System.Web.* 物件,多是方便沿用習慣的寫法或移植舊程式,未來也可能會逐步淘汰。以 HttpUtility為例,在 ASP.NET Core 1.x 還不存在,是 2.0 才加回來的。由此趨勢,建議用新寫法比較好。