EscapeUriString和EscapeDataString的区别是什么?

如果只处理url编码,我应该使用EscapeUriString?

116891 次浏览

始终使用EscapeDataString(有关原因的更多信息,请参阅下面的Livven的回答)

编辑:删除死链接,以说明两者在编码上的差异

加号(+)可以揭示这些方法之间的很多差异。在简单的URI中,加号表示“空格”。考虑查询谷歌中的“happy cat”:

https://www.google.com/?q=happy+cat

这是一个有效的URI(尝试一下),EscapeUriString将不会修改它。

现在考虑用谷歌查询"happy c++":

< a href = " https://www.google.com/?q=happy + c++”rel = " noreferrer " > https://www.google.com/?q=happy + c + + < / >

这是一个有效的URI(尝试一下),但它会产生一个搜索“happy c”的结果,因为两个加号被解释为空格。为了解决这个问题,我们可以将"happy c++"传递给EscapeDataString,看,__abc1:

< a href = " https://www.google.com/?q=happy + c % 2 b % 2 b”rel = " noreferrer " > https://www.google.com/?q=happy + c % 2 b % 2 b < / >

__abc0编码的数据字符串实际上是“happy%20c%2B%2B”;%20是十六进制表示空格字符,%2B是十六进制表示加号字符。

如果你像你应该的那样使用UriBuilder,那么你只需要EscapeDataString来正确地转义整个URI的一些组件。@Livven对这个问题的回答进一步证明了确实没有理由使用EscapeUriString

我发现现有的答案并不令人满意,所以我决定深入挖掘一点来解决这个问题。令人惊讶的是,答案很简单:

有(几乎)没有使用Uri.EscapeUriString的正当理由。如果需要对字符串进行百分比编码,请始终使用Uri.EscapeDataString.*

*请参阅最后一段的有效用例。

为什么会这样?根据文档:

使用EscapeUriString方法准备一个未转义的URI字符串作为URI构造函数的参数。

这真的说不通。根据RFC 2396:

URI总是以“;已转义的”;表单,因为转义或不转义一个完整的URI可能会改变它的语义。

虽然引用的RFC已被RFC 3986淘汰,但这一点仍然成立。让我们通过一些具体的例子来验证一下:

  1. 你有一个简单的URI,像这样:

     http://example.org/
    

Uri.EscapeUriString不会改变它。

  1. 你决定手动编辑查询字符串而不考虑转义:

     http://example.org/?key=two words
    

Uri.EscapeUriString将(正确地)为你转义空间:

    http://example.org/?key=two%20words
  1. 你决定手动编辑查询字符串进一步:

     http://example.org/?parameter=father&son
    

然而,Uri.EscapeUriString不会改变这个字符串,因为它假设&符号表示另一个键-值对的开始。这可能是也可能不是你想要的。

  1. 你决定你实际上想要key参数为father&son,所以你通过转义&来手动修复前面的URL:

     http://example.org/?parameter=father%26son
    

然而,Uri.EscapeUriString也会转义百分比字符,导致双重编码:

    http://example.org/?parameter=father%2526son

正如你所看到的,使用Uri.EscapeUriString来实现它的预期目的,使得不可能使用&作为查询字符串中的键或值的一部分,而是作为多个键-值对之间的分隔符。

这是因为,为了使它适合转义完整的uri,它忽略保留字符,只转义既不是保留字符也不是非保留字符,顺便说一句,这与文档相反。这样你就不会得到像http%3A%2F%2Fexample.org%2F这样的东西,但你最终会得到上面所说明的问题。


最后,如果你的URI是有效的,它不需要转义来作为参数传递给URI构造函数,如果它是无效的,那么调用Uri.EscapeUriString也不是一个神奇的解决方案。实际上,它在许多情况下(如果不是大多数情况的话)都可以工作,但它绝不可靠。

您应该始终通过收集键-值对和百分比编码来构造url和查询字符串,然后将它们与必要的分隔符连接起来。你可以使用Uri.EscapeDataString来实现这个目的,但不能使用Uri.EscapeUriString,因为它不会转义保留字符,如上所述。

只有当你不能这样做时,例如当处理用户提供的uri时,使用Uri.EscapeUriString作为最后的手段才有意义。但是前面提到的警告也适用——如果用户提供的URI是模棱两可的,结果可能不是理想的。

中的注释清楚地说明了这种差异。为什么这些信息没有通过XML文档注释提出,这对我来说是个谜。

EscapeUriString:

此方法将转义不是保留或的任何字符 无保留字符,包括百分号。请注意, EscapeUriString也不会转义'#'符号

EscapeDataString:

此方法将转义非unreserved的任何字符 字符,包括百分号。

所以区别在于它们如何处理保留字符。EscapeDataString逃脱它们;EscapeUriString没有。

根据RFC,保留字符为::/?#[]@!$&'()*+,;=

为完整起见,无保留字符为字母数字和-._~

这两种方法都转义既不是保留字符也不是非保留字符。

我不同意一般的概念认为EscapeUriString是邪恶的。我认为只转义非法字符(如空格)而不转义保留字符的方法是有用的。但它在如何处理%字符方面确实有一个怪癖。百分比编码字符(%后面跟着2个十六进制数字)在URI中是法律。我认为EscapeUriString会更有用,如果它检测到这种模式,并避免编码%时,立即进行2个十六进制数字。

一个简单的例子

var data = "example.com/abc?DEF=あいう\x20えお";


Console.WriteLine(Uri.EscapeUriString(data));
Console.WriteLine(Uri.EscapeDataString(data));
Console.WriteLine(System.Net.WebUtility.UrlEncode(data));
Console.WriteLine(System.Web.HttpUtility.UrlEncode(data));


/*
=>
example.com/abc?DEF=%E3%81%82%E3%81%84%E3%81%86%20%E3%81%88%E3%81%8A
example.com%2Fabc%3FDEF%3D%E3%81%82%E3%81%84%E3%81%86%20%E3%81%88%E3%81%8A
example.com%2Fabc%3FDEF%3D%E3%81%82%E3%81%84%E3%81%86+%E3%81%88%E3%81%8A
example.com%2fabc%3fDEF%3d%e3%81%82%e3%81%84%e3%81%86+%e3%81%88%e3%81%8a
*/

我使用加密字符串作为Url参数(例如http://example.com/Test/myencryptedkey/param2/param3),因此没有c#加密方法可以提供安全的Url参数。我最终使用了以下模式:

< p >在加密: Uri.EscapeDataString(myencryptedkey).Replace('%', '~'); < / p > < p >在解密: Uri.UnescapeDataString(myencryptedkey.Replace('~', '%')); < / p >

注意,在加密期间,替换发生在EscapeDataString()之后,而在解密期间,替换发生在UnescapeDataString()之前;