開發者工具 · URL 編碼器和解碼器
URIError: URI malformed — 為什麼decodeURIComponent會拋出異常以及如何修復它
· 工作原理
url 編碼 JavaScript 錯誤處理
當百分號後面沒有接兩個十六進位數字,或解碼的位元組無效 UTF-8 時,decodeURIComponent 會拋出例外。這篇文章展示了觸發它的輸入以及如何防禦性解碼。
崩潰的百分號-為什麼「100% off」破壞了decodeURIComponent
表格收集折扣代碼「100% off」。 JavaScript 將其傳遞給 URL 解碼器中的decodeURIComponent。此函數拋出 URIError: URI malformed。百分號後面沒有兩個十六進制數字。這完全違反了百分比編碼規則。解碼URIComponent期望每個%開始一個三元組,如%20或%C3。單獨的 % 是一個語法錯誤,它會立即停止執行並引發錯誤。
安全地捕獲此錯誤可以完全防止應用程式崩潰。 URL 來自使用者輸入、重新導向、二維碼和電子郵件。打字錯誤經常發生。帶有 URIError 的崩潰報告會告訴您要快速調查的位置。防禦性解碼使應用程式保持執行,並錯誤日誌對於偵錯變得有用。
兩種失敗 — 格式錯誤的十六進位轉義和無效的 UTF-8 位元組序列
decodeURIComponent 恰好在兩種情況下拋出。第一:轉義序列格式錯誤。後面不接兩個十六進位數字的百分比(0-9、A-F、a-f)。範例:%ZZ、%2, %2g。第二:有效的三元組(如 %E9)解碼為無效的 UTF-8 位元組。首先是格式錯誤。二是語義錯誤。兩者都會立即拋出並停止執行。
UTF-8 對位元組序列有嚴格的規則。位元組 0x80–0xFF 僅出現在多位元組序列中。單一 %E9 不能單獨有效 UTF-8。該孤立位元組會觸發錯誤。格式錯誤很明顯。語意錯誤很微妙,但同樣真實。這兩種情況都需要在生產程式碼中進行 try/catch 處理。
傳統單字節編碼 - 當 %E9 單獨拋出但 %C3%A9 存活時
混亂源自於網路標準的歷史。舊頁面使用拉丁語-1 而不是UTF-8。在拉丁文中-1, %E9 代表é。現代瀏覽器專門使用 UTF-8。 UTF-8 將 é 編碼為 %C3%A9。現代解碼器期望 UTF-8 並拒絕 %E9 作為格式錯誤。這是正確的行為。此錯誤表示來源資料存在問題。
現代共識:UTF-8 無所不在。 URL 標準指定 UTF-8。目前所有瀏覽器都使用 UTF-8。如果您在舊系統中遇到 %E9,請捕獲錯誤並回退到原始字串。不要在現代程式碼中解碼為 Latin-1 。調查資料的來源。
三個輸入,三個錯誤訊息 - 引擎在同一斷弦上有何不同
瀏覽器引擎始終拒絕格式錯誤的輸入,但以不同的方式拒絕單字錯誤。 Chrome 報告「URI 格式錯誤」。 Firefox 報告「格式錯誤的 URI 序列」。 Safari 報告「無法將未定義的物件轉換為物件」。所有三個引擎都拒絕相同的輸入。不同引擎或版本之間的確切訊息措辭並未標準化。永遠不要依賴錯誤文字來指導程式碼邏輯。
切勿對程式決策的錯誤訊息進行字串比對。始終按類型捕獲 URIError。該decodeUrl函數包裝decodeURIComponent並提供一致的代碼INVALID_PERCENT_ENCODING。這命名了確切的問題位置。它可以跨執行時執行,因為它不依賴引擎措辭的變化。這種方法更加可靠和可維護。
安全讀取異常 — try/catch, 驗證與回退模式
最簡單的防禦模式:將decodeURIComponent 包裝在try/catch. 中如果拋出異常,請使用原始字串或替換字元。這可以防止格式錯誤的輸入崩潰。對於查詢值,顯示 URL 編碼形式。對於面向使用者的文字,插入替換字元。這可以防止不良輸入破壞應用程式並保持穩定性。
使用正規表示式進行預先驗證,以提高速度和安全性。解碼前檢查輸入是否僅包含有效的 %XX 三元組。模式 /%[0-9A-Fa-f]{2}/g 捕捉有效的轉義;任何不符的內容都是無效的。對於明顯的垃圾,格式錯誤會快速失敗。 UTF-8 錯誤仍然需要 try/catch. 這一起提供了針對錯誤的全面防禦保護。
無聲失敗隱藏著錯誤-為什麼盲解碼與損壞的編碼一樣危險
微妙的風險:解碼器不會拋出錯誤,而是默默地產生錯誤的文字。使用已棄用的 unescape 的舊程式碼會在記憶體中留下無效的 UTF-8 。文字在螢幕上看起來很好,直到到達嚴格驗證 UTF-8 的系統。現代程式碼會拋出異常,而不是默默地損壞。異常比向下游傳播的無聲資料損壞更清晰、更安全。
假設使用者輸入格式錯誤。始終對通話進行換行。使用原始輸入記錄錯誤以進行偵錯。永遠不要假設每個 % 都是有效的。拼字錯誤和截斷會造成不完整的轉義。視為資料錯誤,而不是邏輯錯誤。防禦性程式碼能夠優雅地承受錯誤的輸入並保持系統的可靠性。
真正的工具會跳過什麼 — 伺服器端框架行為與錯誤復原
伺服器框架比瀏覽器更寬鬆處理格式錯誤的編碼。 Ruby、Python 和 PHP 提供了處理 URL 中無效轉義的配置。有些會自動替換替換字元。其他人默默地丟棄位元組。有些會像 JavaScript 一樣拋出例外。實際行為因開發人員選擇的框架和配置設定而異。
本文僅介紹瀏覽器 JavaScript 行為。如果值從伺服器 API 到達,則伺服器在發送之前已解碼或跳過錯誤。伺服器可以比客戶端更寬容。編寫 API 合約時,指定值是原始值還是預解碼值。 URL 查詢字串應以百分比編碼形式到達; JSON 可以預先解碼。
儘早驗證 — 使用 URL 編碼器和解碼器首先檢查可疑字串
在將可疑 URL 傳遞給decodeURIComponent 之前,貼上到 URL 編碼器和解碼器中。該工具顯示精確的編碼、發現格式錯誤的轉義並解釋錯誤,而不會導致應用程式崩潰。使用 %ZZ、%E9 和 100% 進行測試以查看不同的故障及其確切的錯誤訊息。這需要幾秒鐘的時間並建立信心。
儘早驗證,優雅地捕捉錯誤並記錄損壞的內容。防禦性解碼器和測試工具可讓應用程式保持運作和可調試。 URL 編碼器和解碼器將「格式錯誤的 URI」轉換為您可以立即使用的可操作資訊。將此模式應用於您自己的解碼器,以提高生產環境中的彈性和可維護性。