在 JavaScript 和 Node.js 中使用 URL 應該很簡單,但是我們專案中最近的一個錯誤讓我陷入了 URL 和 URLSearchParams API 中微妙怪癖的兔子洞。這篇文章將探討這些怪癖,它們如何在您的程式碼中引起問題,以及您可以採取哪些措施來避免它們。
我們在生成 URL 並向其添加哈希簽名時遇到了此問題。查詢參數的百分比編碼不一致,導致意外行為和錯誤的雜湊簽章。
很明顯,URL 和 URLSearchParams 物件之間的互動需要格外小心。
第一個驚喜是 URL.search 和 URLSearchParams.toString() 之間的差異。
使用 .searchParams 修改 URL 時要小心,因為根據 WHATWG 規範,URLSearchParams 物件使用不同的規則來決定要對哪些字元進行百分比編碼。例如,URL 物件不會對 ASCII 波形符 (~) 字元進行百分號編碼,而 URLSearchParams 將始終對其進行編碼。
// Example 1 const url = new URL("https://example.com?param=foo bar"); console.log(url.search); // prints param=foo%20bar console.log(url.searchParams.toString()); // prints ?param=foo+bar // Example 2 const myURL = new URL('https://example.org/abc?foo=~bar'); console.log(myURL.search); // prints ?foo=~bar // Modify the URL via searchParams... myURL.searchParams.sort(); console.log(myURL.search); // prints ?foo=%7Ebar
在我們的專案中,我們需要明確地重新指派 url.search = url.searchParams.toString() 以確保查詢字串的編碼一致。
另一個問題是 URLSearchParams 如何處理字元。預設情況下,URLSearchParams 解釋為空格,這在編碼二進位資料或 Base64 字串時可能會導致資料損壞。
const params = new URLSearchParams("bin=E+AXQB+A"); console.log(params.get("bin")); // "E AXQB A"
一種解決方案是在將值附加到 URLSearchParams 之前使用encodeURIComponent:
params.append("bin", encodeURIComponent("E+AXQB+A"));
更多詳細資訊請參閱 MDN 文件。
比較 URLSearchParams.get 和 URLSearchParams.toString 的輸出時會出現另一個微妙之處。例如:
const params = new URLSearchParams("?key=value&key=other"); console.log(params.get("key")); // "value" (first occurrence) console.log(params.toString()); // "key=value&key=other" (all occurrences serialized)
在多值場景中,get 只傳回第一個值,而 toString 則是序列化所有值。
在我們的專案中,我們透過明確重新分配搜尋屬性解決了這個問題:
url.search = url.searchParams.toString(); url.searchParams.set( "hash", cryptography.createSha256HmacBase64UrlSafe(url.href, SECRET_KEY ?? "") );
這確保了在新增雜湊值之前所有查詢參數都已正確編碼。
WHATWG URLSearchParams 介面和 querystring 模組具有類似的用途,但 querystring 模組的用途更通用,因為它允許自訂分隔符號(& 和 =)。另一方面,URLSearchParams API 純粹是為 URL 查詢字串而設計的。
querystring 比 URLSearchParams 效能更高,但不是標準化 API。當效能不重要或需要與瀏覽器程式碼相容時,請使用 URLSearchParams。
與 querystring 模組不同,使用 URLSearchParams 時,不允許數組值形式的重複鍵。陣列使用 array.toString() 進行字串化,它只是用逗號連接所有陣列元素。
// Example 1 const url = new URL("https://example.com?param=foo bar"); console.log(url.search); // prints param=foo%20bar console.log(url.searchParams.toString()); // prints ?param=foo+bar // Example 2 const myURL = new URL('https://example.org/abc?foo=~bar'); console.log(myURL.search); // prints ?foo=~bar // Modify the URL via searchParams... myURL.searchParams.sort(); console.log(myURL.search); // prints ?foo=%7Ebar
使用querystring模組,查詢字串'foo=bar&abc=xyz&abc=123'被解析為:
const params = new URLSearchParams("bin=E+AXQB+A"); console.log(params.get("bin")); // "E AXQB A"
小心 URLSearchParams 處理特殊字元(例如 ~)和空格。必要時使用encodeURIComponent。
了解 URL.search、URLSearchParams.get 和 URLSearchParams.toString 之間的區別,以避免意外行為。
在 Node.js 中,如果要將重複的查詢參數鍵解析為數組,請使用查詢字串模組。
以上是JavaScript 中 URL 和 URLSearchParams 的陷阱的詳細內容。更多資訊請關注PHP中文網其他相關文章!