先分流:你要編碼的是什麼
「URL 編碼」這個搜尋詞背後藏著三種不同的活,選錯模式就是事故的開始:
| 你要編碼的 | 用什麼模式 | 為什麼 |
|---|---|---|
參數值(放進 ?key= 後面的內容) |
元件編碼(encodeURIComponent) | 值裡的 & = ? # 必須轉義,否則 a=1&b=2 裡的 & 會被當成參數分隔符 |
整條 URL(已有 ? 與 & 結構,只想讓中文空格合法化) |
URI 編碼(encodeURI) | 保留 URL 結構字元,只編碼不合法的字元 |
| 表單提交體(application/x-www-form-urlencoded) | 表單編碼 | 空格編碼為 + 而不是 %20——歷史包袱,詳見下文 |
三種模式(URL 編碼解碼器 都支援,切換即對比):
輸入: a=hello world&next=你
component: a%3Dhello%20world%26next%3D%E4%BD%A0 (& = 全轉義,當資料傳)
uri: a=hello%20world&next=%E4%BD%A0 (結構保留,只編碼非法字元)
form: a%3Dhello+world%26next%3D%E4%BD%A0 (空格變 +,表單約定)
一句話記法:編碼的是「資料」用 component,編碼的是「位址」用 uri,走的是「表單」用 form。
為什麼參數必須編碼
不編碼不是「有時候能跑」,是埋雷。四個事故場景:
&截斷參數:值裡出現&,伺服器把它當下一個參數的開始——q=a&b傳過去變成兩個參數,前者被截斷;#截斷查詢:#之後的部分是片段識別符,根本不會發到伺服器——值裡的#會讓後面整段消失;- 空格與中文:部分瀏覽器會兜底編碼,但「部分兜底」意味著行為不可預期,跨端不一致;
%本身:值裡的%不編碼,解碼端把它當轉義引導符,遇%GG這類非法序列直接報錯。
規範的態度很簡單:URL 裡只保證一部分 ASCII 安全,其餘一律百分號編碼。%E4%BD%A0 就是「你」——UTF-8 三個位元組 E4 BD A0,每位元組寫成 % 加兩位十六進位。它不是亂碼也不是加密,是同一內容的合法寫法,一個漢字對應三組 %XX 是 UTF-8 的特徵。
+ 與 %20:一個歷史包袱的兩面
空格有兩種編碼:%20(百分號編碼,規範路徑)和 +(表單編碼,HTML 表單時代的歷史約定,沿用至今)。
事故的典型形態:值裡本來有 +——電話號碼 +86、公式 a+b——伺服器按表單規則解碼,把 + 還原成空格:+8613800000000 變成 8613800000000。更隱蔽的是簽名場景:客戶端用 %20 編碼空格、服務端按表單規則處理成 +,簽名串對不上,校驗失敗——你以為金鑰錯了,其實是空格打架。
排查方法:看請求的 Content-Type——application/x-www-form-urlencoded 意味著走 + 約定,JSON body(application/json)裡空格就是空格,不參與這套規則。解法:編碼端把字面 + 編成 %2B;空格表示兩端統一(都用 %20 或都按表單約定)。帶表單模式的編碼工具可以直接驗證兩端行為是否一致。
排查清單:解碼失敗與雙重編碼
- Invalid URI 報錯:
%後面不是兩位十六進位(%GG、孤立%、被截斷的%E4%BD三缺一)都會讓decodeURIComponent拋錯。先搜孤立%判斷是否截斷; - 雙重編碼:
%被編碼成%25,於是%E4變成%25E4——看到%25開頭的串,先再解一層。成因多是框架編了一次、程式碼裡又手動編了一次; - 解了一層還是亂碼:數一下一個漢字對應幾組
%XX——三組是 UTF-8 正常態,還亂碼說明內容另有編碼層(比如先 Base64 再 URL,逐層剝); - 整條 URL 解碼後結構壞了:對整條 URL 用 component 模式解碼,
&=還原成資料後參數結構就亂了——解碼模式也要與編碼時一致。
真話:編碼解決傳輸,不解決語意
百分號編碼保證的是「這段內容能安全通過 URL 通道」,它不解決參數語意問題:重複 key(a=1&a=2 誰生效是伺服器的事)、陣列參數的序列化格式(a[]=1 vs a=1,2 vs a=1&a=2,是框架約定)、空值與 null 的表示——這些用編碼工具折騰不出來答案,去查你的框架文件。工具的領地是編碼層的排錯:把手裡這串東西編對、解對、看清裡面是什麼。
常見問題
encodeURIComponent 和 encodeURI 有什麼區別,各自什麼時候用?
區別在對待 URL 結構字元的態度:encodeURIComponent 把 & = ? # / 全部轉義——它假設你編碼的是「一段資料」;encodeURI 保留這些結構字元——它假設你編碼的是「一條完整的 URL」。編碼參數值必須用 encodeURIComponent,否則值裡的 & 會被伺服器當成下一個參數的分隔符;編碼整條已有結構的 URL 才用 encodeURI。用反了:前者會把 URL 攔腰打斷,後者會讓參數值被截斷。
URL 參數裡的中文為什麼變成了 %E4%BD%A0 這樣的串?
這是百分號編碼:URL 規範裡只允許一部分 ASCII 字元直接出現,其他內容必須編碼。中文字元先按 UTF-8 轉成位元組——「你」是三個位元組 E4 BD A0——每個位元組再寫成百分號加兩位十六進位,就成了 %E4%BD%A0。它不是加密也不是損壞,是同一內容的另一種寫法;解碼工具一步還原。一個漢字對應三組 %XX 是 UTF-8 的特徵。
加號解碼後為什麼變成了空格?
這是表單編碼的歷史包袱:HTML 表單提交時空格被編碼成 + 而不是 %20,這個約定沿用至今。值裡本來就有 +(電話號碼 +86),伺服器按表單規則解碼就把 + 還原成了空格——簽名校驗對不上、資料損壞都是這麼來的。排查方法:看 Content-Type 是不是 x-www-form-urlencoded;解法是編碼端把 + 編成 %2B,或兩端統一用 %20 表示空格。
解碼時報 Invalid URI 錯誤怎麼回事?
decodeURIComponent 遇到非法百分號序列會直接拋錯——% 後面不是兩位十六進位(%GG、孤立的 %、被截斷的序列)。常見原因三個:編碼層數不對(雙重編碼只解了一次);內容裡有原始 % 字元沒被編碼成 %25;複製貼上時編碼串被截斷。排查順序:先搜孤立 % 看是否截斷,再試再解一次看是否雙重編碼。