Request 接收引數亂碼原理解析

kboypkb發表於2021-09-09

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

 

實際情景:


同事負責的平臺是Ext.js框架搭建的,web.config配置檔案裡配置了全域性為“GB2312”編碼:



當前臺提交“中文文字”時,後臺用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, true, this.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)

internal void FillFromString(string s, bool urlencoded, Encoding encoding)
{
    int num = (s != null) ? s.Length : 0;
    for (int i = 0; i = 0)
        {
            str = s.Substring(startIndex, num4 - startIndex);
            str2 = s.Substring(num4 + 1, (i - num4) - 1);
        }
        else
        {
            str2 = s.Substring(startIndex, i - startIndex);
        }
        if (urlencoded)//外面的傳值預設是true,所以會執行以下語句
        {
            base.Add(HttpUtility.UrlDecode(str, encoding), HttpUtility.UrlDecode(str2, encoding));
        }
        else
        {
            base.Add(str, str2);
        }
        if ((i == (num - 1)) && (s[i] == '&'))
        {
            base.Add(null, string.Empty);
        }
    }
}

說明:

從這點我們發現:所有的引數輸入,都呼叫了一次: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則為原始引數

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1916/viewspace-2800988/,如需轉載,請註明出處,否則將追究法律責任。

相關文章