瀏覽器亂碼
06-02
(轉)Ajax 非同步 請求FF IE區別 默認編碼 如何轉換作者:西門 日期:2010-03-08參數編碼規範一.摘要我們經常要在頁面傳遞中文數據,但是往往被文字編碼所困惑.有時不了解到底是瀏覽器編碼問題還是伺服器編碼問題.本文分析了互聯網傳遞數據的編碼原理, 並且提出了完善易用的解決方案.二.原則避免在get或者post參數時直接傳遞中文字元.中文參數需要經過編碼後再傳遞.伺服器端要使用相同的編碼格式進行解碼三.錯誤觀點1.很多程序員認為url中可以傳遞中文.url中並不能攜帶中文參數.如果我們在瀏覽器中輸入"http://localhost/?a=中文",感覺上我們在url中帶了中文,實際上當按下回車鍵後,瀏覽器自動將其中的"中文"漢字進行編碼後傳遞給伺服器.2.當獲取中文參數產生了亂碼時, 往往首先檢查伺服器端程序的編碼格式.很多人認為url可以傳遞中文,不知道瀏覽器有自動編碼的行為, 所以單純的認為問題出在伺服器端.其實即使在伺服器端找到了正確的編碼格式,我們也不應該輕易地改變伺服器的默認編碼格式.3.傳遞參數前編碼,使用Request對象獲取參數時解碼很多的程序員認為認為,傳遞參數時我們使用UrlEncode等方式編碼, 在接收時應該使用UrlDecode解碼.這是常見的錯誤請大家一定要注意,使用默認的Request.QueryString和Request.Form時已經自動執行了一次解碼,使用的解碼格式是伺服器端設置的默認編碼格式.四.原因傳遞中文字元時,自動的編碼解碼格式和瀏覽器與伺服器的設置有關.測試Firefox3和IE6的Get方式發送中文參數, Firefox默認使用UTF-8格式編碼中文參數, 而IE6即使在高級設置中設置了"總是以 UTF-8 發送URL", 仍然自動使用GB2312編碼中文參數.對於伺服器端我們可以自由的控制解碼的格式.但是往往是通過更改伺服器配置進行全局的統一設置.比如對於ASP.NET程序.可以在Web.Config中設置伺服器段的編碼和解碼格式:<globalization culture="zh-CN" uiCulture="zh-CN" requestEncoding="UTF-8" responseEncoding="gb2312" />但是我們沒法控制瀏覽器端行為.用戶可能使用不同的瀏覽器.五.解決方案1.統一默認的編碼格式(1)設置伺服器端的編碼格式為UTF-8(2)傳遞參數全部進行編碼,.伺服器端(C#)使用Server.UrlEncode方法,客戶端(javascript)使用encodeURIComponent方法.說明:客戶端的javascript函數encodeURIComponent只能使用UTF-8編碼格式. 所以需要設置伺服器端request和response都為UTF-8.缺陷是如果某些合作夥伴必須傳遞其他的編碼格式的參數, 則伺服器端或獲取到亂碼.此方案實現簡單,適合大部分場景.2.通過編碼參數指定編碼格式為了解決可能存在的無法統一編碼格式的問題, 我們使用一個參數"encoding"來顯示的指定編碼格式.encoding參數需要在所有的請求中傳遞,無論是get還是post.(1)對於javascript客戶端編碼而言, 仍然使用encodeURIComponent方法編碼, 此時指定encoding參數的值為"UTF-8".(2) 對於傳入給伺服器端的其他編碼格式, 比如GB2312, 我們不能使用默認的Request.Form或者QueryString方法進行編碼.因為伺服器端的編碼格式可能設置為了UTF-8.此時使用 Request.Form或者QueryString會自動使用伺服器端指定的編碼格式進行解碼. 所以需要使用下面的方法自己處理請求,獲取參數: /// <summary> /// 根據指定的編碼格式返回請求的參數集合 ziqiu.zhang 2009.1.19 /// </summary> /// <param name="request">當前請求的request對象</param> /// <param name="encode">編碼格式字元串</param> /// <returns>鍵為參數名,值為參數值的NameValue集合</returns> public static NameValueCollection GetRequestParameters(HttpRequest request, string encode) { NameValueCollection result = null; Encoding destEncode = null; //獲取指定編碼格式的Encoding對象 if (!String.IsNullOrEmpty(encode)) { try { //獲取指定的編碼格式 destEncode = Encoding.GetEncoding(encode); } catch { //如果獲取指定編碼格式失敗,則設置為null destEncode = null; } } //根據不同的HttpMethod方式,獲取請求的參數.如果沒有Encoding對象則使用伺服器端默認的編碼. if (request.HttpMethod == "POST") { if (null != destEncode) { Stream resStream = request.InputStream; byte[] filecontent = new byte[resStream.Length]; resStream.Read(filecontent, 0, filecontent.Length); string postquery = destEncode.GetString(filecontent); result = HttpUtility.ParseQueryString(postquery, destEncode); } else { result = request.Form; } } else { if (null != destEncode) { result = System.Web.HttpUtility.ParseQueryString(request.Url.Query, destEncode); } else { result = request.QueryString; } } //返回結果 return result; }通過上面的方法, 無論是Get請求還是Post請求, 我們都可以使用自己指定的編碼格式獲取參數.如果有人認為寫這個方法是在自找麻煩,請看"二.錯誤觀點"中的第三條.此方法返回的是一個NameValueCollection對象,判斷是否有某個參數時不能使用檢查是否存在key值的方法.而是要通key獲取值,然後判斷值是否為null(和List有些不同): //獲取參數, 假設paramList是一個NameValueCollection對象 p1= paramList["p1"]; //判斷是否存在此參數,如果不存在則p1為null if ( !( String.IsNullOrEmpty(p1) ) {...}另外本方法如果沒有傳遞Encoding或者傳遞的字元串無法轉換成強類型的Encoding對象, 則使用伺服器端默認編碼格式(即直接使用Request對象的QueryString和Form獲取參數).六.Javascript編碼方法發送請求的一方叫做客戶端.我們經常需要使用Javascript在客戶端編碼中文參數.下面javascript中和編碼有關的函數:函數名稱 函數說明 解釋escape() escape() 函數可對字元串進行編碼,這樣就可以在所有的計算機上讀取該字元串。 該方法不會對 ASCII 字母和數字進行編碼,也不會對下面這些 ASCII 標點符號進行編碼: - _ . ! ~ * " ( ) 。其他所有的字元都會被轉義序列替換。[已過時] 請使用 encodeURI() 或 encodeURIComponent()unescape() unescape() 函數可對通過 escape() 編碼的字元串進行解碼。 該函數的工作原理是這樣的:通過找到形式為 %xx 和 %uxxxx 的字元序列(x 表示十六進位的數字),用 Unicode 字元 u00xx 和 uxxxx 替換這樣的字元序列進行解碼。[已過時] 請使用 decodeURI() 或 decodeURIComponent()encodeURI() encodeURI() 函數可把字元串作為 URI 進行編碼。 該方法不會對 ASCII 字母和數字進行編碼,也不會對這些 ASCII 標點符號進行編碼: - _ . ! ~ * " ( ) 。該方法的目的是對 URI 進行完整的編碼,因此對以下在 URI 中具有特殊含義的 ASCII 標點符號,encodeURI() 函數是不會進行轉義的:;/?:@&=+$,#[提示] 如果 URI 的參數中含有不能轉移的字元,則應當使用 encodeURIComponent() 方法分別對各參數進行編碼。decodeURI() decodeURI() 函數可對 encodeURI() 函數編碼過的 URI 進行解碼。 encodeURIComponent() encodeURIComponent() 函數可把字元串作為 URI 組件進行編碼。 該方法不會對 ASCII 字母和數字進行編碼,也不會對這些 ASCII 標點符號進行編碼: - _ . ! ~ * " ( ) 。其他字元(比如 :;/?:@&=+$,# 這些用於分隔 URI 組件的標點符號),都是由一個或多個十六進位的轉義序列替換的。[提示] 此方法會編碼URI中的特殊字元decodeURIComponent() decodeURIComponent() 函數可對 encodeURIComponent() 函數編碼的 URI 進行解碼。 escape和 unescape在V3版本的標準中已經不在推薦使用.應該用encodeURI和encodeURIComponent方法.對於一個URI(URL也 是一中URI),如果我們希望將它作為完整的網址發送請求, 但是上面帶有中文, 則應該使用encodeURI方法.如果是要編碼參數,則應該使用encodeURIComponent.下面舉例說明這兩個方法的區別:document.write(encodeURIComponent("http://www.w3school.com.cn")+ "<br />")document.write(encodeURI("http://www.w3school.com.cn")+ "<br />")結果http%3A%2F%2Fwww.w3school.com.cnhttp://www.w3school.com.cn七.瀏覽器自動編碼Get請求對於Get方式發送的請求, 不同的瀏覽器使用不同的編碼方式自動為中文參數編碼.比如:Firefox/3.0.5 使用UTF-8, IE6使用GB2312.Post請求對於Post方式發送的請求, 表單中的參數值對是通過request body發送給伺服器,此時瀏覽器會根據網頁的ContentType("text/html; charset=GBK")中指定的編碼進行對錶單中的數據進行編碼,然後發給伺服器。在HTML代碼的Head中添加:<meta http-equiv="Content-Type" content="text/html;charset=gb2312" />Firefox/3.0.5 會使用根據charset中設置的編碼格式編碼post的中文參數.IE6不起作用.實驗表明使用客戶端瀏覽器默認編碼格式具有不確定性.所以傳遞中文時我們要手工編碼參數.八.總結寫這篇文章的目的是提醒Web程序員要注意瀏覽器的自動編碼, 在一個項目中按照本文提供的解決方案將避免中文參數傳遞帶來的亂碼問題.在看了YJingLee"s Blog的"CnBlogs博文排版技巧"後我對本文重新進行了整理.
推薦閱讀:
推薦閱讀:
※Chrome瀏覽器的(PGO)技術是怎麼樣的?
※手機QQ瀏覽器精品美圖
※使用率僅有1%的Chrome應用服務將停止,它為何被谷歌拋棄?