起因:
今天早上被同事问了一个问题:说接收到的参数是乱码,让我帮着解决一下。
 
实际情景:
 
同事负责的平台是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()http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://System.Web:2.0.0.0:b03f5f7f11d50a3a/System.Web.HttpRequest/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:切入:QueryStringEncodinghttp://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://System.Web:2.0.0.0:b03f5f7f11d50a3a/System.Web.HttpRequest/property:QueryStringEncoding:System.Text.Encoding
 
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则为原始参数

详细解析ASP.NET中Request接收参数乱码原理的更多相关文章

  1. Request 接收参数乱码原理解析二:浏览器端编码原理

    上一篇<Request 接收参数乱码原理解析一:服务器端解码原理>,分析了服务器端解码的过程,那么浏览器是根据什么编码的呢? 1. 浏览器解码 浏览器根据服务器页面响应Header中的“C ...

  2. Request 接收参数乱码原理解析三:实例分析

    通过前面两篇<Request 接收参数乱码原理解析一:服务器端解码原理>和<Request 接收参数乱码原理解析二:浏览器端编码原理>,了解了服务器和浏览器编码解码的原理,接下 ...

  3. Request 接收参数乱码原理解析一:服务器端解码原理

    “Server.UrlDecode(Server.UrlEncode("北京")) == “北京””,先用UrlEncode编码然后用UrlDecode解码,这条语句永远为true ...

  4. Request 接收参数乱码原理解析

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

  5. 关于ASP.NET中Request.QueryString的乱码问题(转)

    转自 http://www.cnblogs.com/chinhr/archive/2008/09/23/1296582.html 今天在使用Request.QueryString的时候,发现所有接收到 ...

  6. asp.net中Request请求参数的自动封装

    这两天在测一个小Demo的时候发现一个很蛋疼的问题----请求参数的获取和封装,例: 方便测试用所以这里是一个很简单的表单. <!DOCTYPE html> <html xmlns= ...

  7. Struts2中Action接收参数的方法主要有以下三种:

    Struts2中Action接收参数的方法主要有以下三种: 1.使用Action的属性接收参数(最原始的方式):     a.定义:在Action类中定义属性,创建get和set方法:     b.接 ...

  8. Asp.net中Request.Url的各个属性对应的意义介绍

    Asp.net中Request.Url的各个属性对应的意义介绍 本文转载自 http://www.jb51.net/article/30254.htm 网络上关于Request.Url的说明已经很多也 ...

  9. 用WIN7系统IIS的提示:数据库连接出错,请检查Conn.asp文件中的数据库参数设置

    我用科讯的从4.0开始,去年开始很少用科讯做新站了,今天拿来做一下,结果悲剧了,数据库路径老是不对,百度一番又一番的,,最后终于给度娘解决了.分享出来给遇到同样的问题的人. 用WIN7系统IIS的注意 ...

随机推荐

  1. [python] 网络数据采集 操作清单 BeautifulSoup、Selenium、Tesseract、CSV等

    Python网络数据采集操作清单 BeautifulSoup.Selenium.Tesseract.CSV等 Python网络数据采集操作清单 BeautifulSoup.Selenium.Tesse ...

  2. 转载:Android SQLite数据库版本升级原理解析

    Android使用SQLite数据库保存数据,那数据库版本升级是怎么回事呢,这里说一下. 一.软件v1.0 安装v1.0,假设v1.0版本只有一个account表,这时走继承SQLiteOpenHel ...

  3. 软工实践 - 第十八次作业 Alpha 冲刺 (9/10)

    队名:起床一起肝活队 组长博客:https://www.cnblogs.com/dawnduck/p/10035464.html 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过 ...

  4. linux服务进程管理

    1.查看linux占用内存/CPU最多的进程 查使用内存最多的10个进程 ps -aux | sort -k4nr | head -n 10 或者top (然后按下M,注意大写) 查使用CPU最多的1 ...

  5. 一个自动安装LNMP的简洁Shell脚本

    此脚本在生产服务器上使用了一年多,本脚本崇尚简单唯美,只需要一个脚本就可以在任何一台有网络的服务器上自动配置LNMP.本脚本会在脚本执行目录下,建packages目录用于存放LNMP所需要的软件.大家 ...

  6. js中哪些语句在if语句中默认为真

    结论:js中有一个函数是:Boolean(value)这个函数把一个value值转换成相应的boolean值. 当value为以下值是为true:1.任意的非空字符串 .2.任意的非0数字 而当val ...

  7. JS省份联级下拉框

    <script type="text/javascript"> var china=new Object();china['北京市']=new Array('北京市区' ...

  8. 洛谷T8116 密码

    T8116 密码 题目描述 YJC把核弹发射密码忘掉了……其实是密码被加密了,但是YJC不会解密.密码由n个数字组成,第i个数字被加密成了如下形式:第k小的满足(2^L)|(P-1)且P为质数的P.Y ...

  9. linux下修改mysql数据库编码后无法启动解决办法

    linux下老版本的Mysql修改数据库编码的方法是 修改my.cnf vi /etc/my.cnf 在[client]下添加 default-character-set=utf8 在[mysqld] ...

  10. C++虚函数表解析(基础篇)

    原文:http://blog.csdn.net/haoel/article/details/1948051 一.简介  C++中的虚函数的作用主要是实现了多态的机制.虚函数(Virtual Funct ...