URL的編碼和解碼


萬分感謝原文作者:何必等明天
原文出處:http://www.cnblogs.com/xzwblog/

1 為什么要URL編碼

  • 在因特網上傳送URL,只能采用ASCII字符集
    也就是說URL只能使用英文字母、阿拉伯數字和某些標點符號,不能使用其他文字和符號,即
    只有字母和數字[0-9a-zA-Z]、一些特殊符號$-_.+!*'()[不包括雙引號]、以及某些保留字(空格轉換為+,才可以不經過編碼直接用於URL
    這意味着 如果URL中有漢字,就必須編碼后使用。 但是麻煩的是 標准的國際組織並沒有規定具體的編碼方法,而是交給應用程序(瀏覽器)自己決定。 這導致"URL編碼"成為了一個混亂的領域。
      如果包含中文,其實會自動編碼的,比如Chrome和火狐,“文"和"章"的utf-8編碼分別是"E6 96 87"和"E7 AB A0” ,下圖所示的"%e6%96%87%e7%ab%a0"就是按照順序,在每個字節前加上%而得到的:
      在這里插入圖片描述
    但是不同的瀏覽器可能會有不同的編碼方式,不要將編碼交給瀏覽器。應該用JS在前端對URL編碼,這樣就實現了統一
  • 如果key=value這種傳參方式中,value中包含?``=或者&等符號,url的解析會變得很困難
  • 不同的操作系統、瀏覽器、不同的網頁字符集(charset)有不同的默認編碼方式,要有一個統一格式來發送url,參考文章中舉了4個例子(很有讀的必要)!

2 如何編碼

URL編碼通常也被稱為百分號編碼(percent-encoding),是因為它的編碼方式非常簡單:
使用%加上兩位的字符——0123456789ABCDEF——代表一個字節的十六進制形式。URL編碼要做的,就是將每一個非安全的ASCII字符都被替換為“%xx”格式,
對於非ASCII字符,RFC文檔建議使用utf-8對其進行編碼得到相應的字節,然后對每個字節執行百分號編碼。
如"中文"使用UTF-8字符集得到的字節為0xE4 0xB8 0xAD 0xE6 0x96 0x87,經過Url編碼之后得到"%E4%B8%AD%E6%96%87"。

一些常見的特殊字符換成相應的十六進制的值:

+   %20   
/   %2F   
?   %3F   
%   %25   
#   %23   
&   %26  

2.1 JS的三種編碼函數

上面說了編碼方式的混亂,那么如何統一呢?
**使用Javascript先對URL編碼,或者將可以在后台編碼的參數編碼后再發送給前端使用。然后再向服務器提交,不要給瀏覽器插手的機會,這樣就能保證客戶端只用一種編碼方法向服務器發出請求 **

escape
  js中編碼出生最早的一個,不提倡使用,真正作用是:
返回一個字符的Unicode編碼值,為的是方便他們能在所有計算機上可讀,規則:
所有空格、標點以及其他非ASCII字符都用%xx編碼替換; 例如空格返回的是%20 字符值大於255的字符以%uxxxx格式儲存

encodeURI函數(推薦使用)
  這個函數才是javascript中真正用來對URL編碼的函數
它着眼於對整個URL進行編碼,因此除了常見的符號以外,對其他一些在網址中有特殊含義的符號"; / ? : @ & = + $ , #",也不進行編碼。編碼后,它輸出符號的utf-8形式,並且在每個字節前加上%。
需要注意的是,它不對單引號’編碼
它對應的解碼函數是decodeURI()。

規則就是我上面第二部分所說的,采用utf-8編碼。比如:
在這里插入圖片描述
encodeURIComponent函數(推薦使用)
與encodeURI()的區別是,它用於對URL的組成部分進行個別編碼,而不用於對整個URL進行編碼。
因此,"; / ? : @ & = + $ , #",這些在encodeURI()中不被編碼的符號,在encodeURIComponent()中統統會被編碼,具體的編碼規則是和encodeURI函數是一樣的
它對應的解碼函數是decodeURIComponent()。

實驗:
利用chrome的開發者工具:
在這里插入圖片描述
可以看到第一種,對需要url編碼的部分用encodeURIComponent函數,其他部分不編碼符合要求,即對需要編碼的參數用encodeURIComponent函數最推薦

2.2 我們的問題

遇到的問題:
get請求的路徑參數filePath為:/image/5cf4adbe16ad4fc18ab2259cb86bb14d.png,

在相應的控制器Controller中:

@RequestMapping(path = "/admin/{filePath}")

那么這個請求就變成了:

http://localhost/admin//image/5cf4adbe16ad4fc18ab2259cb86bb14d.png

由於服務器無法解析上面的url,導致400 bad request錯誤

2.3 Java的URLEncoder.encode(“需要編碼的參數”,“UTF-8”)

比較JS的encodeURIComponent函數和Java的URLEncoder.encode(“需要編碼的參數”,“UTF-8”)函數:
//中國/images/head_tripletown.png//!@#$%^&*()進行URL編碼:

//JS的encodeURIComponent函數
javascript:encodeURIComponent("//中國/images/head_tripletown.png//!@#$%^&*()")
"%2F%2F%E4%B8%AD%E5%9B%BD%2Fimages%2Fhead_tripletown.png%2F%2F!%40%23%24%25%5E%26*()"

//Java的URLEncoder.encode("需要編碼的參數","UTF-8")函數
URLEncoder.encode("//中國/images/head_tripletown.png//!@#$%^&*()","UTF-8")
%2f%2f%e4%b8%ad%e5%9b%bd%2fimages%2fhead_tripletown.png%2f%2f!%40%23%24%25%5e%26*()

可以看到一模一樣,因此:
使用Javascript先對URL編碼,或者將可以在后台編碼的參數編碼后再發送給前端使用。

3 為什么兩次編碼

首先看例子,原始請求:

http://localhost/admin/image/filePath//images/head_tripletown.png/200/200

其中,Controller中的映射文件:

@RequestMapping(path = "/admin/image/filePath/{filePath}/{width}/{height}")

對filePath參數一次編碼后,發起URL請求:
請求為:http://localhost/admin/image/filePath/%2fimages%2fhead_tripletown.png/200/200
在攔截器加斷點:

在這里插入圖片描述
毫無反應。。。所以應該在攔截器工作前就對URL進行了解碼

對filePath參數兩次編碼后,發起URL請求:
請求為:http://localhost/admin/image/filePath/%252fimages%252fhead_tripletown.png/200/200
在攔截器加斷點:
在這里插入圖片描述
一次解碼之前:
在這里插入圖片描述
一次解碼之后:
在這里插入圖片描述
獲得了正常回應:
在這里插入圖片描述
兩次編碼的原因:(重點)

  • 一般的原因:解決服務器解碼后亂碼問題

    如果只進行一次encodeURI,得到的是UTF-8形式的URL,服務器端通過request.getParameter()解碼查詢參數(通常是iso-8859-1)就會得到亂碼。

    如果進行兩次encodeURI,第一次編碼得到的是UTF-8形式的URL,第二次編碼得到的依然是UTF-8形式的URL,但是在效果上相當於首先進行了一次UTF-8編碼(此時已經全部轉換為ASCII字符),再進行了一次iso-8859-1編碼,因為對英文字符來說UTF-8編碼和ISO-8859-1編碼的效果相同。在服務器端,首先通過request.getParameter()自動進行第一次解碼(可能是gb2312,gbk,utf-8,iso-8859-1等字符集,對結果無影響)得到ascii字符,然后再使用UTF-8進行第二次解碼,通常使用java.net.URLDecoder("",“UTF-8”)方法。

    兩次編碼兩次解碼的過程為:

    UTF-8編碼->UTF-8(iso-8859-1)編碼->iso-8859-1解碼->UTF-8解碼,編碼和解碼的過程是對稱的,所以不會出現亂碼。

  • 我們的原因:解決400 bad request錯誤

    由於我們發送的請求為:

http://localhost/admin/image/filePath/%2fimages%2fhead_tripletown.png/200/200

服務器端首先進行一次解碼,變為:

http://localhost/admin/image/filePath//images/head_tripletown.png/200/200

在dispatcherservlet(前端控制器,用來查詢映射文件,轉發請求和轉發回應)中查詢映射文件,發現沒有匹配的RequestMapping,就會報400 bad request錯誤

如果兩次編碼:

http://localhost/admin/image/filePath/%252fimages%252fhead_tripletown.png/200/200

服務器端首先進行一次解碼,變為:

http://localhost/admin/image/filePath/%2fimages%2fhead_tripletown.png/200/200

查詢映射文件可以正常轉發,在接收請求后在手動進行一次解碼。

4 擴展

什么是application/x-www-form-urlencoded

它是一種編碼類型。當URL地址里包含非西歐字符的字符串時,系統會將這些字符轉換成application/x-www-form-urlencoded字符串。表單里提交時也是如此,當包含非西歐字符的字符串時,系統也會將這些字符轉換成application/x-www-form-urlencoded字符串,然后在服務器端自動解碼。FORM元素的enctype屬性指定了表單數據向服務器提交時所采用的編碼類型,默認的缺省值是“application/x-www-form-urlencoded。

然而,在向服務器發送大量的文本、包含大量非ASCII字符的文本或二進制數據時這種編碼方式效率很低。這個時候我們就要使用另一種編碼類型“multipart/form-data”,比如在我們在做上傳的時候,表單的enctype屬性一般會設置成“multipart/form-data”。 Browser端<form>表單的ENCTYPE屬性值為multipart/form-data,它告訴我們傳輸的數據要用到多媒體傳輸協議,由於多媒體傳輸的都是大量的數據,所以規定上傳文件必須是post方法,<input>的type屬性必須是file。


關於某些Web容器自動解碼問題

參考
https://blog.csdn.net/qq_27886773/article/details/95078589?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
https://blog.csdn.net/vickyway/article/details/46375971?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM