Request.Url.Query 和 Request.QueryString 有啥区别?

Posted

技术标签:

【中文标题】Request.Url.Query 和 Request.QueryString 有啥区别?【英文标题】:What's the difference between Request.Url.Query and Request.QueryString?Request.Url.Query 和 Request.QueryString 有什么区别? 【发布时间】:2011-01-14 05:56:48 【问题描述】:

我一直在追踪一个 Url Rewriting 应用程序的错误。该错误显示为查询字符串中某些变音符号的编码问题。

基本上,问题在于一个基本上是 /search.aspx?search=heřmánek 的请求被重写为“search=he%c5%99m%c3%a1nek”的查询字符串

正确的值(使用一些不同的工作代码)是将查询字符串重写为“search=he%u0159m%u00e1nek”

注意两个字符串之间的区别。但是,如果您同时发布两者,您会看到 Url Encoding 再现了相同的字符串。直到你使用 context.Rewrite 函数,编码才会中断。损坏的字符串返回 'heÅmánek'(使用 Request.QueryString["Search"] 并且工作字符串返回 'heřmánek'。此更改发生在调用 rewrite 函数之后。

我使用 Request.QueryString(工作)和另一组使用 Request.Url.Query(request.Url 返回一个 Uri 实例)追踪到这组代码。

虽然我已经解决了这个错误,但我的理解有一个漏洞,所以如果有人知道其中的区别,我已经准备好上课了。

【问题讨论】:

【参考方案1】:

您的问题确实引起了我的兴趣,因此我在过去一个小时左右的时间里阅读了一些内容。我不是绝对肯定我找到了答案,但我会把它扔出去看看你的想法。

从我目前所读到的内容来看,Request.QueryString 实际上是“ServerVariables 集合中 QUERY_STRING 变量的解析版本” [reference] ,其中 Request.Url 是(如您所述)封装在Uri 对象。根据this article,Uri 类的构造函数“...解析 [url 字符串],将其置于规范格式,并进行任何所需的转义编码。”

因此,Request.QueryString 似乎使用不同的函数来解析来自 ServerVariables 构造函数的“QUERY_STRING”变量。这可以解释为什么您会看到两者之间的差异。现在,为什么自定义解析函数和 Uri 对象的解析函数使用不同的编码方法完全超出了我的理解。也许更精通 aspnet_isapi DLL 的人可以为这个问题提供一些答案。

无论如何,希望我的帖子有意义。在旁注中,我想添加另一个参考,它也提供了一些非常彻底和有趣的阅读:http://download.microsoft.com/download/6/c/a/6ca715c5-2095-4eec-a56f-a5ee904a1387/Ch-12_HTTP_Request_Context.pdf

【讨论】:

这两个属性大部分时间都返回相同的编码字符串——在这种情况下,构造函数和解析是无关紧要的。只有在 rewrite 调用之后 Uri 的编码才会改变。 也许你的答案和下面的 womps 答案结合起来就是事实。可能是这样的情况,一个使用 Latin-1 代码保留 Url 编码,而另一个使用 UTF-8。【参考方案2】:

您表示的“损坏”编码字符串实际上是根据标准的正确编码。您表示为“正确”编码的那个是对规范使用非标准扩展,以允许%uXXXX 格式(我相信它应该表示 UTF-16 编码)。

无论如何,“损坏”的编码字符串是可以的。您可以使用以下代码进行测试:

Uri uri = new Uri("http://www.example.com/test.aspx?search=heřmánek");
Console.WriteLine(uri.Query);
Console.WriteLine(HttpUtility.UrlDecode(uri.Query));

工作正常。但是...凭直觉,我尝试了 UrlDecode 指定了 Latin-1 代码页,而不是默认的 UTF-8:

Console.WriteLine(HttpUtility.UrlDecode(uri.Query, 
           Encoding.GetEncoding("iso-8859-1")));

...我得到了你指定的错误值,'heÅmánek'。换句话说,看起来对HttpContext.RewritePath() 的调用以某种方式将urlencoding/decoding 更改为使用Latin-1 代码页,而不是UTF-8,这是UrlEncode/Decode 方法使用的默认编码。

如果你问我,这看起来像是一个错误。您可以查看反射器中的RewritePath() 代码,发现它肯定在使用查询字符串——将其传递给各种虚拟路径函数,并传递给一些非托管 IIS 代码。

我想知道是否在某个地方,Request 对象核心的 Uri 被错误的代码页操纵了?这可以解释为什么 Request.Querystring(它只是来自 HTTP 标头的原始值)是正确的,而使用错误编码的变音符号的 Uri 是不正确的。

【讨论】:

很高兴您认为这是一个错误。我无法弄清楚为什么会出现这种行为。【参考方案3】:

过去一天左右我做了一些研究,我想我有一些关于这方面的信息。

当您使用 Request.Querystring 或 HttpUtility.UrlDecode(或 Encode)时,它使用的是在 web.config(或 .config 层次结构,如果您没有的话)的元素(特别是 requestEncoding 属性)中指定的编码指定)---不是 Encoding.Default,它是您服务器的默认编码。

当您将编码设置为 UTF-8 时,单个 unicode 字符可以编码为 2 %xx 十六进制值。当给定整个值时,它也会以这种方式解码。

如果您的 UrlDecoding 使用的编码与 url 的编码不同,您将得到不同的结果。

由于 HttpUtility.UrlEncode 和 UrlDecode 可以采用编码参数,因此尝试使用 ANSI 代码页进行编码很诱人,但如果您有浏览器支持(显然旧版本不支持),UTF-8 是正确的选择UTF-8)。您只需要确保正确设置并且双方都可以正常工作。

UTF-8 似乎是默认编码:(来自.net 反射器 System.Web.HttpRequest)

internal Encoding QueryStringEncoding

    get
    
        Encoding contentEncoding = this.ContentEncoding;
        if (!contentEncoding.Equals(Encoding.Unicode))
        
            return contentEncoding;
        
        return Encoding.UTF8;
    

按照路径查找 this.ContentEncoding 会导致您(也在 HttpRequest 中)

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

要回答您关于 Request.Url.Quer 和 Request.QueryString 之间区别的具体问题...这里是 HttpRequest 如何构建其 Url 属性:

public Uri Url

    get
    
        if ((this._url == null) && (this._wr != null))
        
            string queryStringText = this.QueryStringText;
            if (!string.IsNullOrEmpty(queryStringText))
            
                queryStringText = "?" + HttpEncoder.CollapsePercentUFromStringInternal(queryStringText, this.QueryStringEncoding);
            
            if (AppSettings.UseHostHeaderForRequestUrl)
            
                string knownRequestHeader = this._wr.GetKnownRequestHeader(0x1c);
                try
                
                    if (!string.IsNullOrEmpty(knownRequestHeader))
                    
                        this._url = new Uri(this._wr.GetProtocol() + "://" + knownRequestHeader + this.Path + queryStringText);
                    
                
                catch (UriFormatException)
                
                
            
            if (this._url == null)
            
                string serverName = this._wr.GetServerName();
                if ((serverName.IndexOf(':') >= 0) && (serverName[0] != '['))
                
                    serverName = "[" + serverName + "]";
                
                this._url = new Uri(this._wr.GetProtocol() + "://" + serverName + ":" + this._wr.GetLocalPortAsString() + this.Path + queryStringText);
            
        
        return this._url;
    

您可以看到它使用 HttpEncoder 类进行解码,但它使用相同的 QueryStringEncoding 值。

由于我已经在这里发布了很多代码并且任何人都可以获得 .NET Reflector,所以我将整理其余部分。 QueryString 属性来自 HttpValueCollection,它使用 FillFromEncodedBytes 方法最终调用 HttpUtility.UrlDecode(上面设置了 QueryStringEncoding 值),最终调用 HttpEncoder 对其进行解码。 他们似乎确实使用了不同的方法来解码查询字符串的实际字节,但他们使用的编码似乎是相同的。

有趣的是,HttpEncoder 有这么多似乎做同样事情的函数,所以这些方法可能存在差异,这可能会导致问题。

【讨论】:

以上是关于Request.Url.Query 和 Request.QueryString 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET如何获取url参数

后台请求数据的几个实例(含获取数据的压缩方式的处理)

Request.QueryString.Get有时获取不到参数时

调用系统的相机和图库,并裁剪

网页获取

如何解决错误:java.util.concurrent.ExecutionException: java.lang.RuntimeException: No server to serve reque