繁體中文

開發者工具 · URL 編碼器和解碼器

WHATWG URL 標準與 RFC 3986:為什麼瀏覽器和函式庫不同意

· 背景

url 編碼 標準 開發者工具

瀏覽器和庫在 URL 解析上的分歧
原始 ToolAcre 向量圖

URL 有兩種現行定義,但它們的目的不同。這篇文章解釋了為什麼 WHATWG 編寫了自己的標準,兩者在編碼和解析方面有所不同,以及您的程式碼遵循哪一個。

嚴格程式庫拒絕但瀏覽器愉快載入的 URL — 一個字串,兩個結論

JavaScript 中帶有反斜線的字串可能會被瀏覽器順利地解釋為 URL 路徑的一部分。相同的字串到達 Python 庫後端,但它拒絕解析它,因為不允許使用反斜線。一個 URL,两種不同的結果。兩者都沒有錯——它們遵循不同的標準。 WHATWG 標準描述了瀏覽器對現實世界 URL 的實際操作,包括它們如何處理格式錯誤的輸入。 RFC 3986 定義了 URL 理想情況下應遵循的正式語法。許多基於 RFC 3986 建構的後端程式庫嚴格執行該語法並拒絕其以外的任何內容。

當您在環境之間移動資料時,這種差異很重要。瀏覽器接受的 URL 可能會在後端工具中驗證失敗。了解程式碼實現的標準可以防止調試幻象問題——URL 在一個地方工作正常,但在其他地方卻莫名其妙地失敗,沒有明顯的原因。

為什麼 WHATWG 重新開始 — 描述瀏覽器對格式錯誤的輸入真正做了什麼,而不是有效的輸入

WHATWG 工作小組於 2004 成立,旨在標準化瀏覽器在實踐中實際處理 URL 的方式,而不是定義瀏覽器不會遵循的更嚴格的正式規則。 RFC 2396 描述了正式的語法規範,但瀏覽器實際上從未完全正確地遵循它。現實世界的瀏覽器開發了實用的規則來容忍空格、處理轉義字元以及從 RFC 未預料到或期望的格式錯誤的輸入中恢復。

RFC 3986 到達 2005,具有格式正確的 URL 和嚴格要求的正式語法。瀏覽器實作 WHATWG;後端程式庫通常實作 RFC 3986。

編碼集與保留字元 — URL 標準的每個元件清單如何與 RFC 3986 的類別相關

RFC 3986 將字元分為三類:保留的、未保留的以及必須編碼的所有其他內容。冒號、斜線、問號和哈希等保留字元在 URL 中具有結構意義。非保留字元包括字母、數字、連字符、底線、句點和波形符;這些總是安全的。其他所有內容都以百分比編碼為位元組。這個標準提供了一條明確的規則:知道你的角色屬於哪一類。

WHATWG URL 標準採用基於元件的方法。它為scheme、authority、path、query和fragment分別指定不同的編碼規則,而不是使用全域類別。 「&」符號可能會編碼在路徑中,但會單獨保留在查詢字串中。空間總是被編碼的,但確切的表示因上下文而異。這種按元件設計可以更好地匹配瀏覽器行為,但需要知道您正在編碼 URL 的哪一部分。

容錯:空格、反斜線和製表符 - 輸入一個標準拒絕,另一個修復

空格必須變成 %20,但瀏覽器會默默地轉換文字空格。這兩個標準都禁止使用反斜杠,但某些瀏覽器將它們視為路徑分隔符號。禁止使用製表符、換行符和控製字元。 WHATWG 指定寬鬆的解析器行為:轉換或忽略它們。

非 ASCII 字元(例如 é 或 中)必須使用 UTF-8 編碼進行百分比編碼。 RFC 3986 實際上並未指定字元編碼步驟本身;它假設位元組存在,但沒有說明如何從文字中取得它們。 WHATWG 標準明確要求 UTF-8:先將字串轉換為 UTF-8 位元組,然後對它們進行百分比編碼。兩種標準都達到相同的編碼結果,但它們從不同的基本假設出發,並對於相同的事情並不明確。

工作範例:在兩個模型中解析帶有反斜線和空格的 URL — 輸出比較

以字串「https://example.com/café\ search」為例。瀏覽器遇到反斜線並將其視為路徑字元;它看到空格並將其編碼為 %20,產生類似 https://example.com/café%5C%20search. 的內容 RFC 3986 解析器立即拒絕整個 URL,因為禁止反斜線和空格。瀏覽器繼續解析;嚴格解析器完全停止。試試另一個例子:「https://user@example.com:80/path?q=a&b=c". 兩個標準都清楚地標識了使用者資訊、主機、連接埠、路徑和查詢。它們完全同意這個結構化 URL。只有在異常或格式錯誤的輸入上才會出現分歧。

開啟 URL 編碼器和解碼器,並將 RFC 3986 模式與瀏覽器行為進行比較。貼上帶有空格、反斜線或其他邊緣情況的字串。該工具準確地向您展示了每個標準如何以不同的方式轉換相同的輸入。您可以立即看到哪一個更嚴格以及每個都做什麼。

您的環境使用哪一種 - 瀏覽器和節點遵循 URL 標準;許多伺服器庫都遵循 RFC,一般描述

在瀏覽器中,JavaScript 預設使用 WHATWG URL 標準。 URL API 準確地實現了它。 Node.js 也使用 WHATWG。 Python 函式庫傾向於實作 RFC 3986; urllib 緊跟在後。 Java 庫各不相同; java.net.URL 傾向於 RFC 3986。 Rust 的 url 箱遵循 WHATWG。 Go 的 net/url 受到 WHATWG 的影響。這是一般模式,而不是絕對規則。

當您以程式設計方式建立 URL 並在瀏覽器和後端之間移動時,請選擇標準並堅持使用。使用瀏覽器的 URL API 取得 WHATWG。如果你的後端庫更嚴格,這不是矛盾,而是設計選擇。

這不包括什麼 - 主機名稱解析、IPv6 文字和 IDNA 處理

主機名稱解析涉及 IDNA、punycode 和註冊商規則,完全超出了 URL 解析本身。 IPv6 位址、特殊方案(例如 mailto: 或 data:)以及空元件是與百分比編碼完全不同的單獨主題。網域長度限制和主機名稱有效性因註冊商而異,與本討論無關。也排除:相對引用和特定於方案的解析規則。這篇文章僅關注編碼和解析差異。

本討論重點在於區分這些標準的編碼和解析差異。排除主機名稱規則、DNS 規則和特定於方案的行為可防止對百分比編碼規則產生混淆。

重點:相同的 URL 在一個世界中有效,而在另一個世界中則錯誤 — URL 編碼器和解碼器如何為您提供純 RFC 3986 編碼,以便您可以看到瀏覽器規範化的內容

相同的 URL 字串可以在一種標準下有效,而在另一個標準下無效。兩者在各自的設計目標內都是正確的。以程式設計方式編碼 URL 元件時,請使用適合您環境的工具。 WHATWG 描述了瀏覽器實際執行的操作; RFC 3986 定義形式語法。 URL 編碼器和解碼器顯示 RFC 3986 規則以及瀏覽器行為,以便您可以看到確切的差異並選擇適合您情況的選項。

當 URL 跨越瀏覽器到後端邊界時,最常出現問題。理解這種差異意味著有意識地處理這種交叉,而不是意外或錯誤。