1. 程式人生 > >Form表單(url)中的加號處理

Form表單(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。

問題分析: 

    查了下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 ,程式才能正常處理)
   

問題解決:

思路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: 前面提的這幾種方案,都是基於+ 號是正常的業務場景進行考慮。同時我們也可以從業務層面進行一個梳理,+ 號處理是否有其必要性,能從業務資料入口直接規避 那就最好了。 


背景知識:

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 個) !#$&'()*+,/:;[email protected]_~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 編解碼的時候千萬別被這些假象給迷惑了。

相關推薦

Formurl加號處理

    客戶訂購了一關鍵字為"e+h 變送器" , 在首頁推薦廣告中,會根據使用者在search 搜尋過的關鍵字進行一個匹配投放。技術實現是UED 通過JS 獲取cookie 中的h_keys 內容,拼裝到 http://xxxxx/advert/ctp_advert.htm?num=4&keywo

【Html】在inputid和name的區別

但是name在以下用途是不能替代的: 1. 表單(form)的控制元件名,提交的資料都用控制元件的name而不是id來控制。因為有許多name會同時對應多個控制元件,比如checkbox和radio,而id必須是全文件中唯一的。此外瀏覽器會根據name來設定傳送到伺服器的r

Ant-Design元件:Form

Ant Design of React @3.10.9 拉取專案 luwei.web.study-ant-design-pro, 切換至 add 分支,可看到 Form 表單實現效果 實現一個新增表單 思路 Create表單:@Form.create() 表單資料繫結 get

js的form提交url傳參數包含+等特殊字符的解決方法

字符 www. mit function form表單提交 sub win tno wiki 方法一:(偽裝form表單提交) linkredwin = function(A,B,C,D,E,F,G){ var formredwin = document.cr

jqPaginator分頁ajax用法和form提交用法

用法 () var meta lang 點擊 parse name back 一般使用方法 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8">

SpringMVC4+thymeleaf3的一個簡單例項篇四:form資料驗證

關於表單資料驗證有很多中方法,這裡我僅介紹JSR303註解驗證。 注意在spring的配置檔案spring-mvc.xml中要有這句程式碼:<mvc:annotation-driven/>

使用者註冊的form加校驗

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Trans

MVC提交Form方法一

Views <script src="~/Contents/js/jquery-form.js"></script> <form class="form-horizont

Web應用環境下不同頁面之間的傳值本文暫時只討論 form資料提交

這是我個人在嘗試的多種頁面傳值後選擇的一個form表單傳值的需求。 一、需求背景: 本次開發遇到的是一個H5頁面分別為3個層次頁面,頁面A為資料輸出頁面,頁面B為資料確認頁面,頁面C為注意事項確認及最終確認申請頁面。 二、流程分析: 本人比較笨,所以直接用ProcessOn

【js】Ajax提交formajaxSubmit使用講解

1 、引入依賴指令碼 <script type="text/javascript" src="/js/jquery/jquery.form.js"></script>   /

-EasyUI Spinner 微調器、EasyUI Numberspinner 數值微調器、EasyUI Timespinner 時間微調器、EasyUI Slider 滑塊

origin 範圍 val 描述 vertical seconds 最大值 net auto EasyUI Spinner 微調器 擴展自 $.fn.validatebox.defaults。通過 $.fn.spinner.defaults 重寫默認的 defaults。

關於輸入常用的驗證

content user urn doctype mage tran nav pid clas 現在輸入經常通過ajax提交,所以我也沒有直接寫表單的驗證。凡是輸入其實都是可以驗證的。說到驗證,我們習慣於在輸入之後馬上就能返回結果,在這一點上ajax能有不錯的效果。但是大部

Django 中下- 自定義鉤子進行數據驗證

form hook 在前面的例子裏面 http://beanxyz.blog.51cto.com/5570417/1963702,已經演示了form可以自動地對正則進行合法的判斷,如果不合法,會直接顯示錯誤信息。但是這個功能並不完善,比如說一個數據可能正則判斷合法了,但是不符合唯一性的判斷,那麽怎麽處

使用Formik輕松開發更高質量的React使用指南

from def direct cti timeout lba ica pro nis 基礎 Imagine you want to build a form that lets you edit user data. However, your user API has

使用Formik輕松開發更高質量的React入門

NPU pen node ive ble plus one lex imp 前言 發現Formik是在我學習redux-form過程中從國外一篇博客上偶然發現的,看到作者的高度肯定後我立即轉到github上,正如許多朋友所註意的,我發現其星數達8282,這個數字在git

使用Formik輕松開發更高質量的React<Formik />解析

strong 禁止 修改 new equal 控制 形式 結合 dom 提醒和建議 根據我的粗淺經驗,如果您對Formik感興趣,並且想深入學習與使用這個庫,我建議您還是先對redux-form的使用邏輯與有關概念有所了解,而且理解和使用方面也變得容易得多的多。因為F

使用Formik輕松開發更高質量的React其他幾個API解析

else errors method however obj disable user etc gree (下面部分內容正處於翻譯中,敬請稍等......) <Field /> <Field /> will automagically hook up

HTML

*********************表單和表單域****************************** 一.什麼是表單 收集使用者資訊的一個容器,相當於點餐時的選單... 表單中包含表單域(文字框、密碼框、按鈕 都屬於表單域) 定義表單的語法: <form> 表單域<

JavaScript實現重置reset的方法

轉自:https://www.jb51.net/article/63305.htm 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <script> 5 function formReset() 6 { 7 do

jspform繫結action的方法

其中getUserByAccess是action中的一個方法 <form action="UserAction!getUserByAccess.action" method="post"> <input type="text" na