URL中帶加號的處理

問題原由:  
    客戶訂購了一關鍵字爲"e+h 變送器" , 在首頁推薦廣告中,會根據用戶在search 搜索過的關鍵字進行一個匹配投放。技術實現是UED 經過JS 獲取cookie 中的h_keys 內容,拼裝到 http://xxxxx/advert/ctp_advert.htm?num=4&keyword= {keyword} 。 這裏取出來對應的cookie 信息爲中文,最後經過一個ajax 發起一個GET請求。

    因此針對最後的請求是:http://xxxxxx/advert/ctp_advert.htm?num= 4&keyword=e+h 變送器。 而在服務端接受到對應的請求參數時,發現參數爲:e h 變送器, + 號沒了。 初步懷疑跟URL規範相關,須要進行url encode。html


問題分析: 

    查了下JS encode 的相關內容, 總於發現+ 號的祕密。
   html 中由於一些非標準的作法,將+ 等同於空格進行處理 (當Html 的表單被提交時, 每一個表單域都會被Url 編碼以後纔在被髮送。因爲歷史的緣由,表單使用的Url 編碼實現並不符合最新的標準。例如對於空格使用 的編碼並非%20 ,而是+ 號,若是表單使用的是Post 方法提交的,咱們能夠在HTTP 頭中看到有一個Content-Type 的header ,值爲 application/x-www-form-urlencoded ,大部分應用程序均能處理這種非標準實現的Url 編碼)。
    在搜索引擎中作了下嘗試: 
    keyword =  e h 變送器  , url = http: //www.google.cn/search?hl=zh-CN&newwindow=1&q=e+h變送器    ( 空格被轉化爲+ 號)
    keyword = e+ h 變送器 , url = http: //www.google.cn/search?hl=zh-CN&newwindow=1&q=e%2Bh變送器   (+ 號被進行了轉義爲%2B ,程序才能正常處理)
   ajax


問題解決:

思路1:
    1.  要想正常傳輸+ 號而不被轉義爲空格,須要進行進行編碼爲%2B 。查了下幾個編碼函數,發現只有encodeURIComponent 纔會對+ 號進行編碼處理。
    2. encodeURIComponent 默認爲採用UTF-8 字符集,理論上只須要在原先的請求中添加_input_charset=utf-8(由 pipeline 中的SetLocaleValve 進行解析) ,就能夠獲得正確的 e+h 變送器。 
    
    在實施過程當中,發現結果並非預期的那樣。 客戶端經過js encode 後,在服務端解析後一直是亂碼。 查了下byte ,發現服務端一直是用GBK 在進行解析, 針對變送器的UTF-8 編碼的byte 爲{-27,-113,-104,-23,-128,-127},客戶端用GBK 解析後變爲{-27.-113.- 104.-23,-63,-63} ,針對最後兩byte 由於字符不可見,致使所有被替換爲-63 。網上查了下,針對 utf-8 -> gbk -> utf-8 在必定狀況下就會出現該問題(http://lingqi1818.iteye.com/blog/348953 ) 。跨域


思路2 : 
    繼續追查對應的_input_charset=utf-8 未生效的緣由,DEBUG 看到在SetLocaleValve 中的確設置了request.setCharsetEncoding 爲utf-8 。初步懷疑是否跟jboss server 的配置有關,查了下跟URIEncoding 和useBodyEncodingForURI 設置有關。 目前公司所使用的jboss 爲4.05 ,對應俄tomact 配置中只指定了對應的URIEncoding=GBK 。正由於這樣,致使設置的_input_charset 針對GBK 的提交沒有效果 ,仍是按照GBK 進行解析。

    1.  考慮將請求由GET 換成POST , 這樣就可使用_input_charset 

    但在實施過程當中,和UED 溝經過程,針對POST 的會引發一個跨域請求的問題。此方案又只能作罷瀏覽器


思路3 ( 實踐成功) : 
    
    1.  UED 進行僞url encode 的實現 , 將+ 號進行%2B 的編碼。 由於目前JS 中沒有現成的函數,這裏只是經過replace(/\+/g, '%2B') 進行了轉化。安全


總 結

針對+ 號的處理,針對不一樣的業務場景須要不一樣的處理方案,描述下幾種場景:
1. 非Ajax 請求
    能夠直接使用Form 表單的 GET ,POST 的urlencode 協議,自動實現+ => %2B 的轉化
2.  Ajax 請求
    * GET 請求 : 很無奈,只能使用方案3 ,人爲進行+ 號轉化。
    * POST 請求( 同一應用,非跨域請 求) :  使用encodeURIComponent +  _input_charset=utf-8 指定編碼進行處理。

ps: 前面提的這幾種方案,都是基於+ 號是正常的業務場景進行考慮。同時咱們也能夠從業務層面進行一個梳理,+ 號處理是否有其必要性,能從業務數據入口直接規避 那就最好了。 cookie

 


背景知識:app


URIEncoding 和useBodyEncodingForURI

    對於URL 提交的數據和表單中GET 方式提交的數據,在接收數據的JSP 中設置request.setCharacterEncoding參數是不行的, 由於在Tomcat5.0 中,默認狀況下使用ISO- 8859-1 對URL 提交的數據和表單中GET 方式提交的數據進行從新編碼(解碼),而不使用該參數對URL 提交的數據和表單中GET 方式提交的數據進行 從新編碼(解碼)。要解決該問題,應該在Tomcat 的配置文件的Connector 標籤中設置useBodyEncodingForURI 或者URIEncoding 屬性,其中useBodyEncodingForURI 參數表示是否用 request.setCharacterEncoding 參數對URL提交的數據和表單中GET 方式提交的數據進行從新編碼 ,在默認情 況下,該參數爲false (Tomcat4.0 中該參數默認爲true ); URIEncoding 參數 指定對全部GET 方式請求(包括URL 提交的數據和表單中GET 方式提交的數據)進行統一的從新編碼(解碼)的編碼 。 URIEncoding 和useBodyEncodingForURI 區別是,URIEncoding 是對全部GET 方式的請求的數據進行統一的從新編碼 (解碼),而useBodyEncodingForURI 則是根據響應該請求的頁面的request.setCharacterEncoding 參數對數 據進行的從新編碼(解碼),不一樣的頁面能夠有不一樣的從新編碼(解碼)的編碼。因此對於URL 提交的數據和表單中GET 方式提交的數據,能夠修改 URIEncoding 參數爲瀏覽器編碼或者修改useBodyEncodingForURI 爲true ,而且在得到數據的JSP 頁面中request.setCharacterEncoding 參數設置成瀏覽器編碼。函數


爲何須要Url 編碼
1.  Url 中有些字符會引發歧義 , =,& 號等
2.  Url 的編碼格式採用的是ASCII 碼,而不是Unicode ,這也就是說你不能在Url 中包含任何非ASCII 字符,例如中文


哪些字符須要編碼
RFC3986 文檔規定,Url 中只容許包含英文字母(a-zA-Z )、數字(0-9 )、-_.~4 個特殊字符以及全部保留字符。
Url 能夠劃分紅若干個組件,協議、主機、路徑等。RFC3986 中指定了如下字符爲保留字符: ! * ' ( ) ; : @ & = + $ , / ? # [ ]


如何對Url 中的非法字符進行編碼
Url 編碼一般也被稱爲百分號編碼(Url Encoding ,also known as percent-encoding ),是由於它的編碼方式很是簡單,使用% 百分號加上兩位的字符——0123456789ABCDEF—— 表明一個字節的 十六進制形式。Url 編碼默認使用的字符集是US-ASCII 。例如a 在US-ASCII 碼中對應的字節是0x61 ,那麼Url 編碼以後獲得的就是% 61 ,咱們在地址欄上輸入http: //g.cn/search?q=%61%62%63,實際上就等同於在google 上搜索abc 了。又如@ 符號在 ASCII 字符集中對應的字節爲0x40 ,通過Url 編碼以後獲得的是%40 。工具



Javascript 中的escape,encodeURI 和encodeURIComponent 的區別 

Javascript 中提供了3 對函數用來對Url 編碼以獲得合法的Url ,它們分別是escape / unescape,encodeURI / decodeURI 和encodeURIComponent / decodeURIComponent 。解碼和編碼的過程是可逆的.

兼容性不一樣 
escape 函數是從Javascript1.0 的時候就存在了,其餘兩個函數是在Javascript1.5 才引入的。可是因爲Javascript1.5 已經很是普及了,因此實際上使用encodeURI 和encodeURIComponent 並不會有什麼兼容性問題。

對Unicode 字符的編碼方式不一樣 
這三個函數對於ASCII 字符的編碼方式相同,均是使用百分號+ 兩位十六進制字符來表示。可是對於Unicode 字符,escape 的編碼方式是% uxxxx ,其中的xxxx 是用來表示unicode 字符的4 位十六進制字符。這種方式已經被W3C 廢棄了。可是在ECMA-262 標準中仍然保留着 escape 的這種編碼語法。encodeURI 和encodeURIComponent 則使用UTF-8 對非ASCII 字符進行編碼,而後再進行百分號 編碼。這是RFC 推薦的。所以建議儘量的使用這兩個函數替代escape 進行編碼。

適用場合不一樣 
encodeURI 被用做對一個完整的URI 進行編碼,而encodeURIComponent 被用做對URI 的一個組件進行編碼。

安全字符不一樣 
escape (69 個) */@+-._0-9a-zA-Z   
encodeURI (82 個) !#$&'()*+,/:;=?@-._~0-9a-zA-Z  
encodeURIComponent (71 個) !'()*-._~0-9a-zA-Z   ( 注意+ 號未在其安全字符裏)搜索引擎


其餘和Url 編碼相關的問題對於包含中文的Url 的處理問題,不一樣瀏覽器有不一樣的表現。例如對於IE ,若是你勾選了高級設置「 老是以UTF-8發送Url」 ,那麼Url 中的路徑部分 的中文會使用UTF-8 進行Url 編碼以後發送給服務端,而查詢參數中的中文部分使用系統默認字符集進行Url 編碼。爲了保證最大互操做性,建議全部放到 Url 中的組件所有顯式指定某個字符集進行Url 編碼,而不依賴於瀏覽器的默認實現。另外,不少HTTP 監視工具或者瀏覽器地址欄等在顯示Url 的時候會自動將Url 進行一次解碼(使用UTF-8 字符集),這就是爲何當你在 Firefox 中訪問Google 搜索中文的時候,地址欄顯示的Url 包含中文的緣故。但實際上發送給服務端的原始Url 仍是通過編碼的。你能夠在地址欄 上使用Javascript 訪問location.href 就能夠看出來了。在研究Url 編解碼的時候千萬別被這些假象給迷惑了。

相關文章
相關標籤/搜索