application x-www-form-urlencoded與JS的encodeURIComponent()

application/x-www-form-urlencoded

表單的enctype屬性表示在發送到服務器之前應該如何對錶單數據進行編碼,默認值是application/x-www-form-urlencoded。如果表單提交時使用get請求,瀏覽器或者使用httpClient、okhttp這些組件開發的客戶端會把編碼後的數據加到URL後面,數據和URL使用“?”分隔。如果表單提交時使用post請求,編碼後的數據會被放到請求報文體(body)中提交給服務端

注意,如果是自己使用httpClient、okhttp這些組件開發的客戶端,那麼設置了Content-Type:application/x-www-form-urlencoded; charset=UTF-8,還需要我們自己把要傳的數據編碼,組件不會幫我們處理。

編碼後的數據是一個字符串,字符串中的鍵值對用 & 分隔,並且鍵與值用 = 分隔。如果值包含多字節的字符,那麼該字符將被“%HH”取代,即一個百分比符號和兩個十六進制數字表示的字符串。HH與字符編碼有關,如果 Content-Type 爲 application/x-www-form-urlencoded;charset=UTF-8,那麼該字符將首先被編碼爲UTF-8字節數組,再將每個字節轉換爲HH。

特別要注意,不論字符編碼是什麼,空格都會被編碼爲 +,以下三行代碼等同於使用application/x-www-form-urlencoded進行編碼,都輸出 +。

public static void main(String[] args) throws Exception {

        System.out.println(URLEncoder.encode(" ", "UTF-8"));

        System.out.println(URLEncoder.encode(" ", "GBK"));

        System.out.println(URLEncoder.encode(" ", "BIG5"));

    }

由於RFC1738沒有規定具體的字符編碼,所以各瀏覽器使用的字符編碼不一樣,可能是UTF-8,也可能是系統字符編碼。

 

encodeURIComponent()

JS有三個常用的數據編碼函數。

 

escape()

這個函數是最古老的,已經不推薦使用。實際上,escape()不能直接用於URL的編碼,它的真正作用是返回一個字符的Unicode編碼值。比如"春節"的返回結果是%u6625%u8282,也就是說在Unicode字符集中,"春"是第6625個(十六進制)字符,"節"是第8282個(十六進制)字符。

該方法不會對 ASCII 字母和數字進行編碼,也不會對下面這些 ASCII 標點符號進行編碼: * @ - _ + . / 。其他所有的字符都會被轉義序列替換。在\u0000到\u00ff之間的符號被轉成%xx的形式,其餘符號被轉成%uxxxx的形式。對應的解碼函數是unescape()。所以,"Hello World"的escape()編碼就是"Hello%20World"。因爲空格的Unicode值是20(十六進制)。

 

encodeURI()

encodeURI() 函數可把字符串作爲 URI 進行編碼。該方法不會對 ASCII 字母和數字進行編碼,也不會對這些 ASCII 標點符號進行編碼: - _ . ! ~ * ' ( ) 。該方法的目的是對 URI 進行完整的編碼,因此對於 ;/?:@&=+$,# 這些在 URI 中具有特殊含義的 ASCII 標點符號,encodeURI() 函數是不會進行轉義的。

如果值包含多字節的字符,那麼該字符將被“%HH”取代,即一個百分比符號和兩個十六進制數字表示的字符串,字符編碼爲UTF-8。

 

encodeURIComponent()

encodeURIComponent() 函數可把字符串作爲 URI 組件進行編碼。該方法不會對 ASCII 字母和數字進行編碼,也不會對這些 ASCII 標點符號進行編碼: - _ . ! ~ * ' ( ) 。其他字符(比如 :;/?:@&=+$,# 這些用於分隔 URI 組件的標點符號),都是由一個或多個十六進制的轉義序列替換的。

請注意 encodeURIComponent() 函數 與 encodeURI() 函數的區別之處,前者假定它的參數是 只是URI 的一部分(比如協議、主機名、路徑或查詢字符串),因此 encodeURIComponent() 函數將轉義用於分隔 URI 各個部分的標點符號。

如果值包含多字節的字符,那麼該字符將被“%HH”取代,即一個百分比符號和兩個十六進制數字表示的字符串,字符編碼爲UTF-8。

 

爲什麼有時候JS的encodeURIComponent要連續用兩次?

一般情況下,前端可以使用JS處理數據,encodeURIComponent(parmeName)+"="+encodeURIComponent(parmeValue),Java服務端接收時,  容器自動解碼了, String paramValue = request.getParameter(paramName)。

encodeURIComponent 使用的是 UTF-8 字符編碼來編的,如果 request.getParameter(paramName) 時,容器也按 UTF-8 解的話,結果就是正確的,根本無須在客戶端進行二次的 encodeURIComponent。但是如果 request.getParameter(paramName)時容器沒有按 UTF-8 解嗎的話,就會亂碼。容器按什麼編碼來解碼,取決於 request.setCharacterEncoding()和servlet容器的配置(如Tomcat的Connector標籤的URIEncoding屬性)。

可以使用過濾器設置request.setCharacterEncoding("UTF-8"),並且 修改servlet容器配置(有的不用修改,默認就是UTF-8),讓容器在解 GET 提交的參數時使用 UTF-8,這樣客戶端提交前就不用二次編碼,服務端接收時也只要直接 request.getParameter(paramName) 即可。

爲什麼網上會有人提出在客戶端對字符串重複編碼兩次呢?如果因爲項目需要,不能指定容器使用何種編碼規則來解碼提交的參數。比如需要接收來自不同頁面、不地編碼的參數內容時,這個時候,在客戶端對參數進行二次編碼,可以有效的避開“提交多字節字符"的這個棘手問題。第一次編碼,參數內容便不帶有多字節字符了,成了純粹的 Ascii 字符串。再編碼一次,然後提交。接收時容器自動解一次(容器自動解的這一次,不管是按 GBK 、 UTF-8 還是 ISO-8859-1解碼,都能夠正確的得到結果),然後再在程序中實現一次 解碼 (Java中通常使用 java.net.URLDecoder(***, "UTF-8")) 就可以得到提交的參數值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章