asp.net页面编码和浏览器的选择编码

起因:
今天早上被同事问了一个问题:说接收到的参数是乱码,让我帮着解决一下。
 
实际情景:
 
同事负责的平台是Ext.js框架搭建的,web.config配置文件里配置了全局为“GB2312”编码:

起因:

每个asp.net的朋友都知道,在新版本的visual
studio,在没有任何设置的情况下,新建页面时的默认编码为utf-8

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

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

我们可以从两个地方可以看出:

当前台提交“中文文字”时,后台用Request.QueryString[“xxx”]接收到的是乱码。

 

第一:打开aspx页面,“文件”->“高级保存选项”,如下图,可以看出编码为:Unicode

无论用System.Web.HttpUtility.UrlDecode(“xxx”,”编码类型”)怎么解码都无效。

实际情景:

图片 1

 
原理说明:
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图片 3

第二:找到aspx存放路径,用系统自带的文本编辑器打开,然后“文件”->”另存为”,如下图,可以看出编码为UTF-8

 
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);
       
    }
}

同事负责的平台是Ext.js框架搭建的,web.config配置文件里配置了全局为“GB2312”编码:

图片 4

 
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);
    }
}

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

很多时候我们有些疑问,我们经常在aspx页面的<meta
http-equiv=”Content-Type” content=”text/html; charset=gb2312″
/>强制把carset改为gb2312

说明:
从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);
        }
    }
}

当前台提交“中文文字”时,后台用Request.QueryString[“xxx”]接收到的是乱码。

然后我们在“文件”->“高级保存选项”,可以看出编码为:GB2312(如果你前面把carset改为gb2312,vs会自动在高级保存选项中进行绑定改变),然后编译运行后,右击html“查看源”发现<meta
http-equiv=”Content-Type” content=”text/html; charset=gb2312″
/>没有变化,这时候一切正常

说明:
从这点我们发现:所有的参数输入,都调用了一次:HttpUtility.UrlDecode(str2,
encoding);
 
3:结论出来了
当客户端js对中文以utf-8编码提交到服务端时,用Request.QueryString接收时,会先以globalization配置的gb2312去解码一次,于是,产生了乱码。

无论用System.Web.HttpUtility.UrlDecode(“xxx”,”编码类型”)怎么解码都无效。

下面以IE为例:我们以为此时 “右击浏览器”->“编码” 看到的是
浏览器会选中简体中文,但是事实上,你看到的还是选中的Unicode
(再勾选了‘自动选择’前提下)

所有的起因为:
1:js编码方式为urt-8

复制代码;)

现象已经很明显,但是需要验证浏览器为何会这样,F12调试浏览器,我们发现content-type竟然是“text/html;charset=utf-8”!

2:服务端又配置了默认为gb2312

 

图片 5

3:Request.QueryString默认又会调用HttpUtility.UrlDecode用系统配置编码去解码接收参数。

原理说明:

这个现象至少说了两个问题点:

 
文章补充:
 
1:系统取默认编码的顺序为:http请求头->globalization配置节点-》默认UTF-8

1:首先确定的是:客户端的url参数在提交时,Ext.js会对其编码再提交,而客户端的编码默认是utf-8编码

1、asp.net机制至少在某个地方改变了response的ContentEncoding,导致虽然html页面代码上看到的设置<meta
http-equiv=”Content-Type” content=”text/html; charset=gb2312″
/>并没有生效

2:在Url直接输入中文时,不同浏览器处理方式可能不同如:ie不进行编码直接提交,firefox对url进行gb2312编码后提交。

客户端默认有三种编码函数:escape() encodeURI() encodeURIComponent()

2、浏览器再自动选择编码方式的时候不会优先根据html源码中的所展示的<meta
http-equiv=”Content-Type” content=”text/html; charset=gb2312″
/>代码来决定选择什么编码方式,很明显,以上的现象证明浏览器是优先根据“响应标头-response
header”中的键为“Content-Type”的值来自动选择判断,导致html中的所看到的<meta
http-equiv=”Content-Type” content=”text/html; charset=gb2312″
/>形同虚设。

3:对于未编码“中文字符”,使用Request.QueryString时内部调用HttpUtility.UrlDecode后,由gb2312->utf-8时,

 

以上两个问题点很快得到论证:

如果查不到该中文字符,默认转成”%ufffd”,因此出现不可逆乱码。

2:那为什么用Request.QueryString[“xxx”]接收参数时,收到的会是乱码?

问题1、 出现这个现象,我们知道<META http-equiv=”content-type”
content=”text/html; charset=xxx”>
标签是设置页面编码的,meta标签是属于html的,https标头是服务器以HTTP协议传送HTML信息到浏览器前所送出的字串,所以header发送的内容先到达浏览器
在任意新建一个测试页面,在第一个“}”处设置断点,然后命中断点后再“即时窗口”中写入“Response.ContentEncoding.EncodingName”,按enter执行,输出什么?没错:”Unicode

 
 
4:解决之路
知道了原理,解决的方式也有多种多样了:
1:全局统一为UTF-8编码,省事又省心。
 
2:全局指定了GB2312编码时,url带中文,js非编码不可,如ext.js框架。
这种方式你只能特殊处理,在服务端指定编码解码,
因为默认系统调用了一次HttpUtility.UrlDecode(”xxx”,系统配置的编码),
因此你再调用一次HttpUtility.UrlEncode(”xxx”,系统配置的编码),返回到原始urt-8编码参数
再用HttpUtility.UrlDecode(”xxx”,utf-8),解码即可。

为此,我们必须解开Request.QueryString的原始处理逻辑过程

protected void Page_Load(object sender, EventArgs e)
{

 
5:其它说明:默认对进行一次解码的还包括URI属性,而Request.RawUrl则为原始参数

 

}

我们步步反编绎,

如果了解asp.net生命机制的朋友知道,在执行到Page_Load之前已经执行了很多潜在的初始化事件,类似:Page_Init,LoadViewState,LoadPostData等等,可以想象一定是在某个地方系统为响应页面指定修改了ContentEncoding的值,也就是“响应标头-response
header”中的键为“Content-Type”的值为“UTF-8”

2.1:看QueryString属性的代码:

我们不妨做一个测试,上面说过,我把<meta http-equiv=”Content-Type”
content=”text/html; charset=gb2312″
/>的carset改为gb2312,是没有效果的,那么我如果在Page_Load事件中如下写上代码:

图片 6图片 7

protected void Page_Load(object sender, EventArgs e)
{
Encoding gb2312 = Encoding.GetEncoding;
Response.ContentEncoding = gb2312;
}

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;
    }
}

即我在load事件中再次强制性把响应标头中的“Content-Type”改为gb2312,那么浏览器表现如何呢?

复制代码;)

图片 8

 

图片 9

2.2:切入 FillInQueryStringCollection)()方法

这正是我们想看到的,我相信很多朋友有过中文乱码的情况,我先不说具体乱码的解决方案,但是至少搜索发现很多解决方案是在web.config下添加如下节点,即把网站内所有网页的请求编码和响应编码都改为utf-8

图片 10图片 11

<system.web>
<globalization requestEncoding=”utf-8″ responseEncoding=”utf-8″
/>
</system.web>

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);
        
    }
}

其实,上面案例其实只是单个页面的修改response
Encoding为gb2312,我们也可以在web.config中添加<globalization
requestEncoding=”gb2312″ responseEncoding=”gb2312″ />,即整个网站有效

复制代码;)

问题2、浏览器编码方式是根据“响应标头-response
header”中的键为“Content-Type”的值来自动选择判断,而不会简单的根据你在html中看到的标签值<meta
http-equiv=”Content-Type” content=”text/html; charset=gb2312″
/>来判定,虽然这个标签一般情况下会写入header,但是有时候会被暗中修改掉,导致html中看到的和调试捕捉到的Content-Type不一致的情况

 

当然在老版本的ie中,有时候出现的页面全部为空白,右击ie浏览器编码发现没有勾选“自动选择”的情况下会出现这种白屏现象,那不是本文讨论的范围,但是简单的说下原因:老版本的ie浏览器解析网页编码时以HTML内的标签优先,而后才是HTTP
header内的讯息,而mozilla系列的浏览器则刚刚相反,由于UTF-8为3个字节表示一个汉字,而普通的GB2312或BIG5是两个。页面输出时,由于上述原因,使浏览器解析、输
出<title$amp;>amp;$lt;/title>的内容时,如果在</title>前有奇数个全角字符时,IE把UTF-8当作两
个字节解析时出现半个汉字的情况,这时该半个汉字会和</title>的<结合成一个乱码字,导致IE无法读
完<title>部分,使整个页面为空百输出。而这个时候如果察看源文件的话,会发现实际上整个叶面全部已经输出了

2.3:切入:QueryStringEncoding

解决方法:将<meta http-equiv=”Content-Type” content=”text/html;
charset=utf-8″
/>放在<title>测试标题</title>之前(好像现在新建网页默认都在title之前)

图片 12图片 13

asp.net
URL参数编码

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);
    }
}

几乎所有的朋友都会遇到中文情况下乱码的问题,其原因到底是为何?

复制代码;)

我先不说乱码问题,先说get和post的区别,几乎没有人不知道

说明:

我们在新建asp.net页面时,是很少去对form进行修改的,即保持默认的<form
runat=”server”>,可是编译运行后查看代码发现变成<form method=”post”
action=”Default2.aspx” >

从QueryStringEncoding代码得出,系统默认会先取globalization配置节点的编码方式,如果取不到,则默认为UTF-8编码方式

很显然,asp.net默认会为form的method写上post,但是需要注意的是如果仅仅是单纯的html页面,form默认的method是get

 

我这边可以举一个例子诠释一些无关紧要但是又比较重要的东西:

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

情况1:

图片 14图片 15代码有点长,就折叠起来了

浏览器选择编码 :utf-8

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);
        }
    }
}

编译前的aspx : <form
runat=”server”>

说明:

运行后的html : <form
action=”Default2.aspx” method=”post”>

从这点我们发现:所有的参数输入,都调用了一次:HttpUtility.UrlDecode(str2, encoding);

点击服务器按钮:F12在请求正文中有如下图内容

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注