解決ASP.NET中的各種亂碼問題
閱讀目錄
開始
頁面顯示亂碼問題
AJAX提交的資料亂碼問題
JavaScript中正確的URL編碼方式
ASP.NET中正確的URL編碼方式
正確的URL編碼方式的總結
徹底解決encodeURIComponent()與GB2312亂碼問題
Cookie亂碼問題
下載檔名亂碼問題
多語言資料的亂碼問題
亂碼問題的總結
經常發現有人被亂碼困擾著,而我感覺比較幸運,很少為此煩惱過。
在這篇部落格中,我將把我想到的一些與亂碼有關的經驗總結出來,供大家參考。
回到頂部
頁面顯示亂碼問題
在一個網站中,有些頁面會正常顯示,然而,有些頁面會顯示成亂碼。如果發生這種情況,可以檢查一下web.config和檔案編碼。
如果web.config是這樣配置的:
而檔案的編碼不是UTF-8:
那麼就會有亂碼問題。
注意:反之是不是會出現亂碼的。
1. 不設定fileEncoding,此時不會有亂碼現象。
2. fileEncoding="gb2312",檔案以utf-8編碼,此時也不會有亂碼現象。
因此,我建議最好讓所有檔案都以UTF-8編碼儲存,從而解決這類亂碼問題。
回到頂部
AJAX提交的資料亂碼問題
AJAX技術流行了這麼多年了,我想現在沒有幾個網站不使用這種技術的。然而,有些人在使用AJAX時,遇到了亂碼問題。
透過分析這類亂碼案例中,我發現幾乎都是採用這種方式向服務端提交資料: “key1=” + escape(value1) +“&key2=” + escape(value2)
這種方法在多數情況下,的確能夠正常工作,然而遇到一些特殊字元,就行不通了。原因我後面再來解釋。
我為這類不正確的方法準備了一個示例 (為了保持示例簡單,我演示一個拼接URL),
頁面程式碼如下:
服務端的程式碼就是從QueryString讀取那些引數值,然後輸出。由於程式碼實在太簡單,就不貼出了。(可下載示例程式碼)
當我點選連結時,服務端返回了這樣的結果:
注意:"fish li + is me." 中間的加號沒有了。
解決這個問題有個簡單的方法,那就是使用JQuery的$.param()方法,修改後的程式碼如下:
另外,我非常反感拼接這種提交資料:“key1=” + escape(value1) +“&key2=” + escape(value2)
因為這種程式碼的可讀性太差了,在此,我建議在AJAX呼叫時,最好直接使用JQuery的$.ajax方法向服務端提交資料。
請看下面的示例程式碼(注意我為data屬性賦值的方式):
執行結果:
回到頂部
JavaScript中正確的URL編碼方式
看過前面的示例,您有沒有想過:為什麼escape不能解決的問題,JQuery就能解決呢?
對於這個問題,我想還是先來看看MSDN中關於escape的說明(截圖):
MSDN說的很清楚,我也沒有必要再做解釋。
不過,我想有人可能會問:我用POST提交資料呢?那可是不經過URL的。
是的,POST資料時,引數沒有放在URL中,但是,仍然採用URL編碼。
POST資料也採用URL編碼,是因為:表單可以採用GET方式提交,那麼資料將透過URL提交給伺服器。
所以提交的資料都要經過URL編碼。
我們再來看一下$.ajax是如何處理資料的提交過程的:
ajax: function( origSettings ) { var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings); // ............... 去掉一些無關的程式碼 // convert data if not already a string if (s.data && s.processData && typeof s.data !== "string") { // 注意下面這個呼叫 s.data = jQuery.param( s.data, s.traditional ); }
再來看jQuery.param的實現過程:
// Serialize an array of form elements or a set of// key/values into a query stringparam: function( a, traditional ) { var s = []; // ............... 去掉一些無關的程式碼 // If an array was passed in, assume that it is an array of form elements. if ( jQuery.isArray(a) || a.jquery ) { // Serialize the form elements jQuery.each( a, function() { add( this.name, this.value ); }); } else { // ............... 去掉一些非重點程式碼 } // Return the resulting serialization return s.join("&").replace(r20, "+"); function add( key, value ) { // If value is a function, invoke it and return its value value = jQuery.isFunction(value) ? value() : value; s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value); }}
這段程式碼的核心就是add函式的實現了,它在內部呼叫了encodeURIComponent()函式。
我們應該注意JQuery對資料的處理方式:encodeURIComponent(key) + "=" + encodeURIComponent(value);
JQuery在最後還把%20還替換成 + 號了。
在WEB開發領域,我想大家對JQuery的權威應該不用懷疑吧? 所以我認為JQuery的方法肯定是正確的。
從JQuery的實現方式也可以看出:encodeURI()其實也是不推薦在編碼URL資料時使用的。
說到這裡,我要說說為什麼不推薦使用encodeURI。
encodeURI用於對整個URL字串進行編碼,如果某個引數值本身包含一些特殊字元。
例如:key = "x?x/x&x", value = "aa=2&bb=3&cc=漢字。",這個函式的結果將會不正確。
它通常用於編碼URL路徑中包含有類似漢字這種場合,不適合處理URL引數。
但是,URL路徑中的目錄名與檔名,我們可以選擇英文字元,所以encodeURI通常沒有機會使用。
回到頂部
ASP.NET中正確的URL編碼方式
前面介紹了JavaScript中三種URL的編碼方式,在服務端,ASP.NET有更多的URL編碼方法,
今天我也把服務端的編碼也做了個總結,因為我發現網上有些資料也是錯誤的。
在ASP.NET中提供三個URL編碼方法:HttpUtility.UrlPathEncode、HttpUtility.UrlEncode、Server.UrlEncode
.NET framework還提供了System.Uri這個類,它也有一些用於URL處理的方法。比如EscapeUriString,EscapeDataString這二個方法,可用於URL路徑與引數的編碼任務。
面對這些方法,我該選擇哪個?
我建議在 ASP.NET 中,編碼查詢引數 時選擇HttpUtility.UrlEncode(str) ,
並且在拼接URL時,採用 HttpUtility.UrlEncode(key) + "=" + HttpUtility.UrlEncode(value) 的方法。
如果要 編碼URL中的路徑,請使用HttpUtility.UrlPathEncode()
下面我來解釋前面不建議使用的另外的一些方法的原因:
1. Server.UrlEncode: 這個方法其實也是呼叫HttpUtility.UrlEncode,只是它會盡量使用Response.ContentEncoding所表示的編碼格式,然而HttpUtility.UrlEncode(str)總是會使用UTF-8編碼,如果你不希望被字元編碼糾纏,那就應該放棄Server.UrlEncode ,畢竟UTF-8編碼才是更好的選擇。
2. 雖然System.Uri的那二個編碼方法,也能實現我們需要的URL編碼任務,但是,當ASP.NET在填充Request.QueryString, Request.Form時,使用的解碼方法是HttpUtility.UrlDecode,因此,如果你執意選擇使用System.Uri的相關的編碼方法,顯然就不能與解碼方法匹配,後果如何就難說了。
回到頂部
正確的URL編碼方式的總結
由於編碼函式(方法)較多,而且又比較重要,我認為有必要再做個總結。
一個完整的URL可以簡單地認為包含二個部分:檔案路徑(含目錄) 和 查詢引數(QueryString)
在編碼時,一定要分開處理。
編碼檔案路徑時,應該選擇 encodeURI,HttpUtility.UrlPathEncode 。
編碼查詢引數時,應該選擇 encodeURIComponent,HttpUtility.UrlEncode,而且拼接方式應該是:Encode(key) + "=" + Encode(value)
絕對不能先把整個URL(包含查詢引數)拼接起來了,再來考慮該選擇哪個編碼方法。
再說一遍:在JavaScript中使用escape肯定是錯誤的。
回到頂部
徹底解決encodeURIComponent()與GB2312亂碼問題
前面我建議在JavaScript中使用encodeURIComponent()來處理提交資料,然而encodeURIComponent()在編碼字元時,使用的是UTF-8編碼。也正因為這個原因,有人可能會說:我的網站使用的編碼方式是gb2312 !
對於這個回答,我有時實在不想再說下去了:你就不能把網站的編碼改成UTF-8嗎?
現在好了,我設計了一種方法,可以解決在GB1212編碼的網站中使用encodeURIComponent(),這個方法的設計思路比較直接:既然encodeURIComponent()是使用UTF-8編碼,那麼,我們是不是隻要告訴服務端,客戶端提交的資料是UTF-8編碼的,此時服務端只要識別後,按照UTF-8編碼來解碼,問題就解決了。
理清了思路,程式碼其實很簡單。首先來看客戶端的程式碼。
$.ajax({ // 注意下面這行程式碼,它為請求新增一個自定義請求頭 beforeSend: function(xhr) { xhr.setRequestHeader("x-charset", "utf-8"); }, url: "/TestParam.ashx", type: "GET", cache: false, data: { id: 2, name: "fish li + is me.", tel: "~!@#$%^&*()_+-=?|", "x?x!x&x": "aa=2&bb=3&cc=漢字。", // 特殊的鍵名,值內容也特殊。 encoding: "見鬼去吧。?& :)", 中文鍵名: "大明王朝1368" }, success: function(responseText) { $("#divResult").html(responseText); }});
注意:在原來的基礎上,我只加了一行程式碼:
beforeSend: function(xhr) { xhr.setRequestHeader("x-charset", "utf-8"); },
再來看服務端程式碼。我寫了一個HttpModule來統一處理這個問題。
public class ContentEncodingModule : IHttpModule{ public void Init(HttpApplication app) { app.BeginRequest += new EventHandler(app_BeginRequest); } void app_BeginRequest(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; HttpWorkerRequest request = (((IServiceProvider)app.Context) .GetService(typeof(HttpWorkerRequest)) as HttpWorkerRequest); // 注意:我並沒有使用 app.Request.Headers["x-charset"] // 因為:絕大部分程式不訪問它,它將一直保持是 null, // 如果我此時該問這個集合,會導致填充它。 // 我認為填充Headers集合比我下面的呼叫的成本要高很多, // 所以,直接透過HttpWorkerRequest讀取請求頭對效能的損耗會最小。 string charset = request.GetUnknownRequestHeader("x-charset"); if( string.Compare(charset, "utf-8", StringComparison.OrdinalIgnoreCase) == 0 ) // ASP.NET在填充QueryString,Form時,會訪問Request.ContentEncoding做為解碼時使用的字元編碼 app.Request.ContentEncoding = System.Text.Encoding.UTF8; }
改造後的結果是:除非客戶端明確新增"x-charset"請求頭,否則還是按原來的方式處理,對於服務端程式碼來說,完全不用修改。
說明:
1. 如果網站的提交全部採用JQuery,也可以統一設定,這是JQuery支援的功能。
2. 如果使用JQuery1.5以上版本,也可以寫成:headers: {"x-charset" : "utf-8"}
3. 就算以後網站使用UTF-8編碼,所有程式碼不需要做任何修改。
回到頂部
Cookie亂碼問題
前段時間,有人在部落格的評論中問我:asp.net伺服器端寫中文cookie,js客戶端讀取時亂碼。
其實這個問題還是比較好解決的,方法是:寫Cookie時用HttpUtility.UrlEncode編碼,然後在客戶端使用decodeURIComponent把內容轉回來就可以了。在此,我推薦使用jquery.cookie.js這個外掛來讀寫Cookie。 示例程式碼如下(前端):
$(function() { var cookie = $.cookie("TestJsRead"); $("#cookieValue").text(cookie);});
服務端程式碼:
cookie = new HttpCookie("TestJsRead", HttpUtility.UrlEncode("大明王朝1368"));Response.Cookies.Add(cookie);
回到頂部
下載檔名亂碼問題
有時我們需要在程式執行時動態的建立檔案,並讓使用者下載這個在執行時產生的檔案,然而,有時候使用者會要求程式能生成一個預設的檔名,方便他們儲存。此時,我們只需要設定Content-Disposition這個響應頭,並給一個預設的檔名就可以了。
一般說來,我們只要讓預設的下載檔名是英文及數字,問題永遠不會出現,但是,有時候使用者可能要求預設的檔案中包含漢字,最終,問題也隨之發生了。請看下面的程式碼:
public void ProcessRequest(HttpContext context){ byte[] fileContent = GetFileContent(); context.Response.ContentType = "application/octet-stream"; string downloadName = "ClownFish效能測試結果.xlsx"; string headerValue = string.Format("attachment; filename="{0}"", downloadName); context.Response.AddHeader("Content-Disposition", headerValue); context.Response.OutputStream.Write(fileContent, 0, fileContent.Length);}
這段程式碼在我的FireFox, Opera, Safari, Chrome都能正常執行,其中FireFox顯示的下載對話方塊也是我期待的樣子:
遺憾的是,在我的IE8中是這樣的:
對於這個亂碼問題,我們需要把程式碼做一點修改:
string downloadName = "ClownFish效能測試結果.xlsx";if( context.Request.Browser.Browser == "IE" ) downloadName = HttpUtility.UrlPathEncode(downloadName);
此時IE顯示的檔名就不是亂碼了。
說明:我的機器環境是 Windows Server 2003 SP2, 用於測試的瀏覽器版本分別為:
回到頂部
多語言資料的亂碼問題
現在還有一種亂碼問題是:同一個程式供多種不同字符集(語言)的使用者在使用。
例如:程式是簡體中文的,此時,繁體中文的使用者無法儲存繁體漢字(就算簡體漢字能正常顯示)。
當發現這種現象時,需要檢查一下資料庫的欄位型別,是否是Unicode或者UTF-8,因為當資料欄位的字符集不支援多種語言時,亂碼問題必定產生。
我建議在使用SQL SERVER時,儲存文字的欄位都使用N開頭的型別,如:nvarchar, nchar,除非明確知道要儲存郵政編碼或者md5值,才有必要使用char(xxx)這種資料型別。類似的,在MySQL中,我建議使用UTF-8
回到頂部
亂碼問題的總結
ASP.NET的亂碼問題一般與二個因素有關:
1. 選擇了不恰當的字元編碼,如:gb2312
2. 選擇了不正確的URL編碼方法,如:escape()
因此,解決方案其實也不難:
1. 字元編碼選擇 utf-8 ,包含檔案編碼,請求/響應編碼,資料庫欄位型別。
2. URL編碼方法選擇encodeURIComponent,再次強烈推薦直接使用JQuery
我一直認為:正確的方法可以讓我在無形中避開許多問題。
如果你還為亂碼問題而煩惱,我建議你先想想你是否選擇了不正確的編碼(方法)。
點選此處下載示例程式碼
如果,您認為閱讀這篇部落格讓您有些收穫,不妨點選一下右下角的【推薦】按鈕。
如果,您希望更容易地發現我的新部落格,不妨點選一下右下角的【關注 Fish Li】。
因為,我的寫作熱情也離不開您的肯定支援。
感謝您的閱讀,如果您對我的部落格所講述的內容有興趣,請繼續關注我的後續部落格,我是Fish Li 。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2618/viewspace-2803325/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 完美解決jspdf各種中文亂碼問題JS
- flashfxp 亂碼,2種辦法解決flashfxp 亂碼問題
- java中亂碼問題解決方法Java
- 解決plsql中中文亂碼問題SQL
- java中解決request中文亂碼問題Java
- 解決CentOS 中顯示亂碼問題CentOS
- URL地址中的中文亂碼問題的解決
- 解決SSH亂碼問題
- 解決中文亂碼問題
- 解決URL請求中的中文亂碼問題
- 解決 plsql 遇到亂碼的問題SQL
- oracle字元亂碼問題的解決Oracle字元
- 解決Flex裡的亂碼問題Flex
- 【IDL】IDL中亂碼問題的解決方法
- MySql中文亂碼問題解決MySql
- Jmeter 解決中文亂碼問題JMeter
- Java 解決中文亂碼問題Java
- RDSSQLSERVER解決中文亂碼問題SQLServer
- 解決MySQL中文亂碼問題MySql
- MYSQL亂碼問題解決方法MySql
- 解決confluence的亂碼問題
- DES加密中文亂碼問題的解決加密
- CentOS中文亂碼問題的解決方法CentOS
- javascript中的各種問題JavaScript
- 解決mac 中的myeclipse控制檯中文亂碼問題MacEclipse
- 解決Oracle11g中的索引名字亂碼問題Oracle索引
- cat 輸出亂碼問題解決
- TongWeb下亂碼問題解決思路Web
- 解決使用Git Bash亂碼問題Git
- plsql查詢亂碼問題解決SQL
- 解決Mysql匯入亂碼問題MySql
- 徹底解決程式亂碼問題
- springmvc 解決中文亂碼問題SpringMVC
- js解決url中文亂碼問題JS
- 解決Intellij IDEA中執行緩慢的問題,tomcat控制檯列印亂碼問題,國際化亂碼配置檔案亂碼解決IntelliJIdeaTomcat
- 解決Spring中ResponseBody返回中文亂碼問題Spring
- Java Web(二) Servlet中response、request亂碼問題解決JavaWebServlet
- Ubuntu中解決pdf中文亂碼或不顯問題Ubuntu