C# 什么';Request.Url.Query和Request.QueryString之间的区别是什么?

C# 什么';Request.Url.Query和Request.QueryString之间的区别是什么?,c#,.net,url,url-rewriting,query-string,C#,.net,Url,Url Rewriting,Query String,我一直在追踪一个Url重写应用程序的错误。这个错误显示为查询字符串中某些变音字符的编码问题 基本上,问题是一个基本上是/search.aspx?search=heřmánek的请求被重写为“search=he%c5%99m%c3%a1nek” 正确的值(使用一些不同的工作代码)是对querystring的重写,即“search=he%u0159m%u00e1nek” 请注意这两个字符串之间的差异。但是,如果您同时发布这两个字符串,您将看到Url编码复制了相同的字符串。直到使用context.Re

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

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

正确的值(使用一些不同的工作代码)是对querystring的重写,即“search=he%u0159m%u00e1nek”

请注意这两个字符串之间的差异。但是,如果您同时发布这两个字符串,您将看到Url编码复制了相同的字符串。直到使用context.Rewrite函数,编码才会中断。断开的字符串返回'heÅmÃnek'(使用Request.QueryString[“Search”]),工作字符串返回'heřmánek'。此更改在调用重写函数后发生

我使用Request.QueryString(工作)和Request.Url.Query(Request.Url返回Uri实例)跟踪到一组代码


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

你的问题确实激发了我的兴趣,所以我在过去的一个多小时里读了一些书。我不是绝对肯定我找到了答案,但我会把它扔出去看看你怎么想

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

因此,Request.QueryString似乎使用了不同的函数来解析“QUERY\u字符串”ServerVariables构造函数中的变量。这将解释为什么您会看到这两者之间的差异。现在,我完全不明白为什么自定义解析函数和Uri对象的解析函数使用不同的编码方法。也许对aspnet_isapi DLL稍有了解的人可以提供一些关于这个问题的答案激动

无论如何,希望我的文章有意义。顺便说一句,我想添加另一个参考资料,也提供了一些非常彻底和有趣的阅读:

您所表示的“断开”编码字符串实际上是符合标准的正确编码。您所表示的“正确”编码编码是使用规范的非标准扩展来允许
%uxxx
格式(我相信它应该表示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));
工作正常。但是……凭直觉,我尝试使用指定的拉丁语-1代码页而不是默认的UTF-8进行UrlDecode:

Console.WriteLine(HttpUtility.UrlDecode(uri.Query, 
           Encoding.GetEncoding("iso-8859-1")));
…我得到了您指定的错误值“heÅmÃnek”。换句话说,它看起来像是对
HttpContext.RewritePath()
的调用以某种方式将urlencoding/decoding更改为使用拉丁-1代码页,而不是UTF-8,UTF-8是UrlEncode/Decode方法使用的默认编码

如果你问我的话,这看起来像个bug。你可以查看reflector中的
RewritePath()
代码,看到它肯定在玩querystring——将它传递给各种虚拟路径函数,并传递给一些非托管IIS代码


我想知道,在这个过程中,请求对象核心的Uri是否被错误的代码页操纵?这就解释了为什么
Request.Querystring
(这只是HTTP头中的原始值)这是正确的,而Uri对变音符号使用错误的编码是不正确的。

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

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

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

如果使用与url编码不同的编码对url进行解码,则会得到不同的结果

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

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

按照路径查找this.ContentEncoding将引导您找到(也在HttpRequest中)

要回答您关于Request.Url.query和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有这么多
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);
    }
}
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;
    }
}