惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
V
V2EX
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
小众软件
小众软件
Vercel News
Vercel News
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
J
Java Code Geeks
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog
B
Blog
美团技术团队
量子位

博客园 - 蹲在路边写代码

8000401a错误解决方案(Office Word,Excel,PowerPoit应用程序无法访问) SQL Server 2005 无法删除用户的解决方法 遍例指定目录及子目录下的所有文件添加进数据库 改善SQL语句的效率的几条原则,不断积累中 一些常用的WebServices 转Javascript的IE和Firefox兼容性汇编 - 蹲在路边写代码 - 博客园 IE的showModalDialog与FireFox的window.open 父子窗口传值示例 三种常见选项卡效果 - 蹲在路边写代码 - 博客园 C#高级编程学习笔记(五)字符串与正则表达式 C#高级编程学习笔记(四) 内存管理和指针 SQL SELECT 语句完整的执行顺序 C#高级编程学习笔记(三)委托和事件 C#高级编程学习笔记(二)运算符和类型转换 C#高级编程学习笔记(-)C#基础 对象和类型 继承 构造函数和析构函数的原理及在C#中的运用(转载) CSS在IE与Firefox下的兼容性 CSS Hack 汇总 .NET的ORM工具SubSonic2.03的配置 ASP.NET的页面间传值方法 - 蹲在路边写代码 - 博客园
转--使用 Request.QueryString 接受参数时,跟编码有关的一些...
蹲在路边写代码 · 2009-04-03 · via 博客园 - 蹲在路边写代码

今天遇到一个问题,Default.aspx?u=%B6%B9%C8%F3%B8%FD

后台这样写

Response.Write(HttpUtility.UrlDecode(Request.QueryString["u"], System.Text.Encoding.UTF8));

Response.Write(HttpUtility.UrlDecode("%B6%B9%C8%F3%B8%FD", System.Text.Encoding.UTF8)); 
得到的结果是前面是乱码,后面正常显示中文。后来查到下面一篇文章才找到原因,解决方法如下:
System.Collections.Specialized.NameValueCollection nv =HttpUtility.ParseQueryString(Request.Url.Query, Encoding.GetEncoding("GB2312"));
 Response.Write(nv["u"]);
默认情况下,要注意编码:比如把form插入到163邮箱或其他中文网页,一般默认是GB2312编码。
参考文章如下,

  使用Request.QueryString接受参数时,跟编码有关的一些问题,我们先来看以下几个请求,看a.aspx页面用Request.QueryString接受到的是啥信息?

       页面URL                     Request.QueryString["info"]接受到的值
案例一 a.aspx?info=%25              %
案例二 a.aspx?info=%bc%bc%ca%f5     ????

情况分析:

  案例一

  a.aspx?info=%25为何Request.QueryString["info"]接受到的值是 % ,而不是 %25,是因为Request.QueryString替我们在接受到值后,做了一次URL解码。HttpUtility.UrlDecode("%25")的计算结果就是 % 。

  上面的这个案例一虽然看起来很简单。但是我们在一些特殊场景时候,就会因为这个而极度郁闷。

  比如以下几种情况:

  你有一个自己的加密算法,而这个加密算法,某些情况下会计算出带百分号的结果,而这个结果你是要通过url参数的方式传递给其它页面的。这时候你就苦恼的发现,某些时候某个功能就不能用。如果解决案例一碰到的情况呢?

  解决方案一:

  把需要传递的参数传递前作一次HttpUtility.UrlEncode,记得是按照UTF-8的编码的UrlEncode。这样如果我们希望客户端接受到的是%25  就应该传递的是%2525 。

  切记,不可在接受方每次接受后,自作聪明的都做一次UrlEncode。而是在发送方做UrlEncode。如果接受方接受后作UrlEncode的话,就会出现下面情况:

  • 发送方发送 a.aspx?info=%25 ,这时候如果接受方  接受后作 UrlEncode 的话,一切正确
  • 发送方发送 a.aspx?info=% ,这时候如果接受方  接受后作 UrlEncode 的话,则就乱了。

  另:这套方案中切记,UrlEncode和UrlDecode的次数应该一一对应。不能多一次,也不能少一次。有人就会说,这还会出现次数不对应么? 比如下面情况,一个不留意就很可能出现次数不对应。而出现不是你所期望的情况。比如我们有这样类似的功能:

  a.aspx页面中,根据传入的from参数,自动跳转到from参数(用Request.QueryString["from"]来接受这个参数)设置的页面。
  b.aspx页面也是同样的逻辑,根据传入的from参数(用Request.QueryString["from"]来接受这个参数),自动跳转到指定的页面。
  c.aspx页面也是同样的逻辑,根据传入的from参数(用Request.QueryString["from"]来接受这个参数),自动跳转到指定的页面。

  这样我们就可能书写下面的链接地址:
  a.aspx?from=b.aspx  
  a.aspx?from=b.aspx?from=c.aspx
  a.aspx?from=b.aspx?from=c.aspx?from=http://blog.joycode.com/ghj/

  下面再复杂一点,我给下面几个链接,其中都有a这个参数,请告诉我a这个参数是被那个页面接受到了?
  说明: HttpUtility.UrlEncode("&")  == "%26" HttpUtility.UrlEncode("%")  == "%25"

  地址 a 参数会被那个页面接受到 
  a.aspx?from=b.aspx?from=c.aspx&a=1 a 参数被 a.aspx 页面接受到了 
  a.aspx?from=b.aspx?from=c.aspx%26a=1 a 参数被 b.aspx 页面接受到了 
  a.aspx?from=b.aspx?from=c.aspx%2526a=1 a 参数被 c.aspx 页面接受到了

  如果想不明白,就想想下面这句话
  每一次用Request.QueryString获取参数时候,就作了一次HttpUtility.UrlDecode。

  解决方案二:

  不用Request.QueryString,而是自己实现一个获取查询参数的方法。细节我在案例二讲完后再告诉大家,因为这个解决方案也处理了案例二的一些情况。

  案例二

  a.aspx?info=%bc%bc%ca%f5传给我们的信息其实是使用GB2312编码后的“技术”这两个汉字。不信,你可以用下面表达式计算的结果就是%bc%bc%ca%f5 HttpUtility.UrlEncode("技术", System.Text.Encoding.GetEncoding("GB2312")) asp.net系统内部,在处理Request.QueryString等情况时候,都是使用的UTF-8的编码,我们如果不存在多系统并存的问题时候,这个问题一点都不存在。但是,当需要跟其它系统交互式后,问题就可能会出现。如果你不了解案例二这里情况时,你就会被这个问题苦恼死。

  比如下面这两个地址提到的问题:

  asp.net中的server.urlencode函数和asp中的server.urlencode函数返回的值竟然不一样,php与aspx之间中文通过url如何传递?

  案例二的解决方案
  使用带编码的 HttpUtility.ParseQueryString 函数

  就是采用类似下面代码的方式,来获得指定格式编码的查询文本参数。

  System.Collections.Specialized.NameValueCollection nv =
System.Web.HttpUtility.ParseQueryString(Request.Url.Query, System.Text.Encoding.GetEncoding("GB2312")); 
  Response.Write(nv["Tag"]);

  要说我为啥知道上面几种解决方案,是因为我用 Reflector 看了 Request.QueryString 的实现代码。在查看代码时候,我们会看到这样一个 internal 方法:
  System.Web.HttpValueCollection 类的内部方法:
  internal void FillFromString(string s, bool urlencoded, Encoding encoding)

  这个内部方法实现了,按需解密查询参数的功能,但是遗憾的是,在querystring 的处理函数中,强制指定了解析 QueryString 时,必须作一次 HttpUtility.UrlDecode。参看如下代码:

public static NameValueCollection ParseQueryString(string query, Encoding encoding)
{
    ...
    return new HttpValueCollection(query, false, true, encoding);
}

  如果我们不想采用案例一的解决方案一,我们就需要自己写一个解析查询信息的代码。我们完全可以照抄 System.Web.HttpValueCollection 类的 internal void FillFromString(string s, bool urlencoded, Encoding encoding) 方法来改写。但郁闷的是:如果你用 Reflector 察看这个函数的实现时候,Reflector 出来的代码是错误的。正确的方法如下:是在施凡帮助下完成的。

  自己实现从 URL 查询文本 Query 中解析出我们自己需要的文本的方法

/// <summary>
/// 根据 URL 中的 查询文本 Query 解析成一个 NameValueCollection
/// 在装配脑袋帮助下 郭红俊 改编自 System.Web.HttpValueCollection 类的内部方法:
/// internal void FillFromString(string s, bool urlencoded, Encoding encoding)
/// </summary>
/// <param name="query">需要解析的查询文本</param>
/// <param name="urlencoded">解析文本时候是否需要URL解码</param>
/// <param name="encoding">解析文本时候,按照那种URL编码进行解码</param>
/// <returns></returns>
public static NameValueCollection FillFromString(string query, bool urlencoded, Encoding encoding)
{
    NameValueCollection queryString = new NameValueCollection();
    if (string.IsNullOrEmpty(query))
    {
        return queryString;
    }

    // 确保 查询文本首字符不是 ?
    if (query.StartsWith("?"))
    {
        query = query.Substring(1, query.Length - 1);
    }

    int num1 = (query != null) ? query.Length : 0;
    // 遍历每个字符
    for (int num2 = 0; num2 < num1; num2++)
    {
        int num3 = num2;
        int num4 = -1;
        while (num2 < num1)
        {
            switch (query[num2])
            {
                case '=':
                    if (num4 < 0)
                    {
                        num4 = num2;
                    }
                    break;
                case '&':
                    goto BREAKWHILE;
            }
            num2++;
        }

    BREAKWHILE:

        string name = null;
        string val = null;
        if (num4 >= 0)
        {
            name = query.Substring(num3, num4 - num3);
            val = query.Substring(num4 + 1, (num2 - num4) - 1);
        }
        else
        {
            val = query.Substring(num3, num2 - num3);
        }
        if (urlencoded)
        {

            queryString.Add(HttpUtility.UrlDecode(name, encoding), HttpUtility.UrlDecode(val, encoding));
        }
        else
        {
            queryString.Add(name, val);
        }
        if ((num2 == (num1 - 1)) && (query[num2] == '&'))
        {
            queryString.Add(null, string.Empty);
        }
    }

    return queryString;

}

  用上面的代码,我们就可以按需解析自己需要的查询参数,而不是受限的使用request.querystring 。

  小结

  Request.QueryString 替我们件事情:每次接受到参数后,都做 UrlEncode ,并且是按照 UTF-8编码做的 UrlEncode 。 这在大多数情况下没有任何问题,但是一些情况下,会给我们带来麻烦,本文就是分析这些可能给我们带来麻烦的场景,以及解决方法。