抓到一個誤用 Request.Url.ToString() 造成的 Bug。
由使用者回報錯誤,追出某段程式擷取 Request.Url.ToString() 加工產生的連結有誤。 原本 URL 參數包含另一頁網址,依規定做了 UrlEncode,如: b=http%3A//blog.darkthread.net%3Fm%3D1%26a%3D456 。ASP.NET 程式抓取 Request.Url.ToString() 修改後串接額外參數"&a=ABC",得到的結果卻是 b=httq://blog.darkthread.net/?m=1&a=456&a=ABC [註: 此處改成 httq 是為防止被當成可連結網址],也因此,接收網頁抓取的 a 變成 456,ABC。
實測後發現,我一直誤認 Request.Url.ToString() 傳回的會是 URL 原始字串(就是瀏覽器地址欄顯示的字串內容),事實不然,來看一個測試:
依據 MSDN 文件:
The value returned by this property differs from ToString and AbsoluteUri. ToString returns the canonically unescaped form of the URI. AbsoluteUri returns the canonically escaped form of the URI.
要將 Uri 型別轉為字串,ToString()、AbsoluteUri、OriginalString都是選項,但意義與用途不同。
Uri.ToString() 傳回的是 URI 以標準方式未逸出處理(Unescaped)過的版本
Uri.AbsoluteUri 傳回的則是 URI 以標準方式逸出處理(Escaped)過的版本
Uri.OriginalString 則是未經處理的原始版本(OriginalString 序列化後不會保留)
換言之,OriginalString 是建構物件時所提供的字串參數,原汁原味連不符規範的部分也都保留;ToString() 及 AbsoluteUri 則為標準化處理過的版本, ToString() 為方便人類閱讀將 %XX 編碼還原回字元,但可能因此不符合 URL 規範,用於瀏覽器可能會出錯(就如本案例); AbsoluteUri 產生的結果則力求符合規範,遺漏 UrlEncode 的部分也會補上,例如以下範例:
var uri = new Uri("http://blog.darkthread.net?a=a c"); //參數空白未編碼
Debug.WriteLine(uri.AbsoluteUri); //會轉為http://blog.darkthread.net/?a=a%20c
在本案例中,AbsoluteUri 是較佳選擇,而實務上應避免使用 ToString(),以免產生無效 URL。