Request 接收参数乱码原理解析

起因:

今天早上被同事问了一个问题:说接收到的参数是乱码,让我帮着解决一下。

 

实际情景:

同事负责的平台是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,  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  <  num; i ++ )
    {
        
int  startIndex  =  i;
        
int  num4  =   - 1 ;
        
while  (i  <  num)
        {
            
char  ch  =  s[i];
            
if  (ch  ==   ' = ' )
            {
                
if  (num4  <   0 )
                {
                    num4 
=  i;
                }
            }
            
else   if  (ch  ==   ' & ' )
            {
                
break ;
            }
            i
++ ;
        }
        
string  str  =   null ;
        
string  str2  =   null ;
        
if  (num4  >=   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则为原始参数

 

 

 

最后做一下链接:路过秋天版博客 V2.0 测试版发布 公测一周

你可能感兴趣的:(Request 接收参数乱码原理解析)