Request 接收參數亂碼原理解析

轉載出處:http://www.cnblogs.com/cyq1162/archive/2010/11/29/1891124.html


起因:

今天早上被同事問了一個問題:說接收到的參數是亂碼,讓我幫着解決一下。

 

實際情景:

複製代碼
同事負責的平臺是Ext.js框架搭建的,web.config配置文件裏配置了全局爲“GB2312”編碼:

<globalization requestEncoding="gb2312" responseEncoding="gb2312" fileEncoding="gb2312" culture="zh-CN"/>

當前臺提交“中文文字”時,後臺用Request.QueryString[
"xxx"]接收到的是亂碼。

無論用System.Web.HttpUtility.UrlDecode(
"xxx","編碼類型")怎麼解碼都無效。
複製代碼

 

原理說明:

1:首先確定的是:客戶端的url參數在提交時,Ext.js會對其編碼再提交,而客戶端的編碼默認是utf-8編碼

客戶端默認有三種編碼函數:escape() encodeURI() encodeURIComponent()

 

2:那爲什麼用Request.QueryString["xxx"]接收參數時,收到的會是亂碼?

爲此,我們必須解開Request.QueryString的原始處理邏輯過程

 

我們步步反編繹,

2.1:看QueryString屬性的代碼:

複製代碼
public NameValueCollection QueryString
{
    
get
    {
        
if (this._queryString == null)
        {
            
this._queryString = new HttpValueCollection();
            
if (this._wr != null)
            {
                
this.FillInQueryStringCollection();//重點代碼切入點
            }
            
this._queryString.MakeReadOnly();
        }
        
if (this._flags[1])
        {
            
this._flags.Clear(1);
            ValidateNameValueCollection(
this._queryString, "Request.QueryString");
        }
        
return this._queryString;
    }
}
複製代碼

 

2.2:切入 FillInQueryStringCollection()方法

複製代碼
private void FillInQueryStringCollection()
{
    
byte[] queryStringBytes = this.QueryStringBytes;
    
if (queryStringBytes != null)
    {
        
if (queryStringBytes.Length != 0)
        {
            
this._queryString.FillFromEncodedBytes(queryStringBytes, this.QueryStringEncoding);
        }
    }
//上面是對流字節的處理,即文件上傳之類的。
    else if (!string.IsNullOrEmpty(this.QueryStringText))
    {
        
//下面這句是對普通文件提交的處理:FillFromString是個切入點,編碼切入點是:this.QueryStringEncoding
        this._queryString.FillFromString(this.QueryStringText, truethis.QueryStringEncoding);
        
    }
}

複製代碼

 

2.3:切入:QueryStringEncoding

複製代碼
internal Encoding QueryStringEncoding
{
    
get
    {
        Encoding contentEncoding 
= this.ContentEncoding;
        
if (!contentEncoding.Equals(Encoding.Unicode))
        {
            
return contentEncoding;
        }
        
return Encoding.UTF8;
    }
}
//點擊進入this.ContentEncoding則爲:
public Encoding ContentEncoding
{
    
get
    {
        
if (!this._flags[0x20|| (this._encoding == null))
        {
            
this._encoding = this.GetEncodingFromHeaders();
            
if (this._encoding == null)
            {
                GlobalizationSection globalization 
= RuntimeConfig.GetLKGConfig(this._context).Globalization;
                
this._encoding = globalization.RequestEncoding;
            }
            
this._flags.Set(0x20);
        }
        
return this._encoding;
    }
    
set
    {
        
this._encoding = value;
        
this._flags.Set(0x20);
    }
}
複製代碼

說明:

從QueryStringEncoding代碼得出,系統默認會先取globalization配置節點的編碼方式,如果取不到,則默認爲UTF-8編碼方式

 

2.4:切入  FillFromString(string s, bool urlencoded, Encoding encoding)

代碼有點長,就摺疊起來了

說明:

從這點我們發現:所有的參數輸入,都調用了一次:HttpUtility.UrlDecode(str2, encoding);

 

3:結論出來了

當客戶端js對中文以utf-8編碼提交到服務端時,用Request.QueryString接收時,會先以globalization配置的gb2312去解碼一次,於是,產生了亂碼。

所有的起因爲:

1:js編碼方式爲urt-8

2:服務端又配置了默認爲gb2312

3:Request.QueryString默認又會調用HttpUtility.UrlDecode用系統配置編碼去解碼接收參數。

 

文章補充

複製代碼
1:系統取默認編碼的順序爲:http請求頭->globalization配置節點-》默認UTF-8

2:在Url直接輸入中文時,不同瀏覽器處理方式可能不同如:ie不進行編碼直接提交,firefox對url進行gb2312編碼後提交。

3:對於未編碼“中文字符”,使用Request.QueryString時內部調用HttpUtility.UrlDecode後,由gb2312->utf-8時,

如果查不到該中文字符,默認轉成
"%ufffd",因此出現不可逆亂碼。

複製代碼

 

 

4:解決之路

知道了原理,解決的方式也有多種多樣了:

1:全局統一爲UTF-8編碼,省事又省心。

 

2:全局指定了GB2312編碼時,url帶中文,js非編碼不可,如ext.js框架。

這種方式你只能特殊處理,在服務端指定編碼解碼,
因爲默認系統調用了一次HttpUtility.UrlDecode("xxx",系統配置的編碼),
因此你再調用一次HttpUtility.UrlEncode("xxx",系統配置的編碼),返回到原始urt
-8編碼參數
再用HttpUtility.UrlDecode("xxx",utf-8),
解碼即可。

 

5:其它說明:默認對進行一次解碼的還包括URI屬性,而Request.RawUrl則爲原始參數

 


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