Liv*_*ven 204

我没有发现现有的答案令人满意,所以我决定深入挖掘一下这个问题.令人惊讶的是,答案非常简单:

没有正当理由可以使用Uri.EscapeUriString.如果需要对字符串进行百分比编码,请始终使用Uri.EscapeDataString.

为什么是这样?根据文件:

使用EscapeUriString方法将非转义URI字符串准备为Uri构造函数的参数.

这没有多大意义.根据RFC 2396:

URI总是处于"转义"形式,因为转义或取消转换已完成的URI可能会改变其语义.

虽然引用的RFC已被RFC 3986淘汰,但重点仍然存在.让我们通过查看一些具体的例子来验证它:

  1. 你有一个简单的URI,如下所示:

    http://example.org/
    
    Run Code Online (Sandbox Code Playgroud)

    Uri.EscapeUriString 不会改变它.

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

    http://example.org/?key=two words
    
    Run Code Online (Sandbox Code Playgroud)

    Uri.EscapeUriString 将(正确)为你逃离空间:

    http://example.org/?key=two%20words
    
    Run Code Online (Sandbox Code Playgroud)
  3. 您决定进一步手动编辑查询字符串:

    http://example.org/?parameter=father&son
    
    Run Code Online (Sandbox Code Playgroud)

    但是,此字符串不会更改Uri.EscapeUriString,因为它假定&符号表示另一个键值对的开始.这可能是也可能不是你想要的.

  4. 您确定您实际上想要key参数father&son,因此您可以通过转义&符来手动修复以前的URL:

    http://example.org/?parameter=father%26son
    
    Run Code Online (Sandbox Code Playgroud)

    但是,Uri.EscapeUriString也将转义百分比字符,导致双重编码:

    http://example.org/?parameter=father%2526son
    
    Run Code Online (Sandbox Code Playgroud)

如您所见,使用Uri.EscapeUriString其预期目的使得无法将其&用作查询字符串中的键或值的一部分,而不是作为多个键值对之间的分隔符.

这是因为,在错误的尝试使其适合转义完整的URI时,它会忽略保留字符,并且只转义既不保留也不保留的字符,BTW与文档相反.这样你最终得不到类似的东西http%3A%2F%2Fexample.org%2F,但你最终会遇到上面说明的问题.


最后,如果您的URI有效,则不需要将其转义为作为参数传递给Uri construtor,如果它无效,那么调用Uri.EscapeUriString也不是一个神奇的解决方案.实际上,它可以在许多情况下工作,但不是大多数情况,但它绝不可靠.

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

  • 哇,谢谢你最后澄清这个问题.前两个答案并不是很有帮助. (4认同)
  • 很好地解释了+1它比接受的链接唯一的答案更好 (4认同)
  • 非常正确.EscapeUriString(类似于EscapeUrl在Win32中的默认行为)是由不了解URI或转义的人创建的.这是一个错误的尝试,创建一个带有格式错误的URI和*有时*将其转换为预期版本.但它没有可靠的信息.它也经常被用来代替EscapeDataString,这也是非常有问题的.我希望EscapeUriString不存在.每次使用它都是错误的. (3认同)

Jcl*_*Jcl 105

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

编辑:删除了两个编码方式不同的死链接

  • 这是一个糟糕的答案.你永远不应该使用EscapeUriString,它没有任何意义.请参阅下面的Livven的答案(并进行投票). (7认同)
  • ...做了一些测试看起来我想要一个URI参数的EscapeDataString.我测试了字符串"I heart C++",而EscapeUriString没有对"+"字符进行编码,它只是将它们保留为原样,EscapeDataString正确地将它们转换为"%2B". (4认同)
  • 我不确定链接实际上是否提供了更多的信息,因为它涉及到unescaping而不是esacaping. (3认同)
  • 我还不清楚 - 如果我没有转义整个URI而只是其中的一部分 - (即查询字符串参数的*data*),该怎么办?我是否在逃避URI的数据,或者EscapeDataString是否意味着完全不同的东西? (2认同)

Set*_*eth 54

加号(+)可以揭示这些方法之间的差异.在简单的URI中,加号表示"空格".考虑向Google查询"快乐猫":

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

这是一个有效的URI(试一试),EscapeUriString不会修改它.

现在考虑查询Google的"happy c ++":

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

这是一个有效的URI(尝试它),但它产生了对"happy c"的搜索,因为这两个加号被解释为空格.要修复它,我们可以将"happy c ++"传递给EscapeDataString*:

https://www.google.com/?q=happy+c%2B%2B

*)编码数据字符串实际上是"happy%20c%2B%2B"; %20是空格字符的十六进制,%2B是加号字符的十六进制.

如果您正在使用UriBuilder,那么您只需要EscapeDataString正确地转义整个URI的某些组件.@Livven对这个问题的回答进一步证明了没有理由使用EscapeUriString.


Lea*_*ing 7

一个简单的例子

\n\n
var data = "example.com/abc?DEF=\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\\x20\xe3\x81\x88\xe3\x81\x8a";\n\nConsole.WriteLine(Uri.EscapeUriString(data));\nConsole.WriteLine(Uri.EscapeDataString(data));\nConsole.WriteLine(System.Net.WebUtility.UrlEncode(data));\nConsole.WriteLine(System.Web.HttpUtility.UrlEncode(data));\n\n/*\n=>\nexample.com/abc?DEF=%E3%81%82%E3%81%84%E3%81%86%20%E3%81%88%E3%81%8A\nexample.com%2Fabc%3FDEF%3D%E3%81%82%E3%81%84%E3%81%86%20%E3%81%88%E3%81%8A\nexample.com%2Fabc%3FDEF%3D%E3%81%82%E3%81%84%E3%81%86+%E3%81%88%E3%81%8A\nexample.com%2fabc%3fDEF%3d%e3%81%82%e3%81%84%e3%81%86+%e3%81%88%e3%81%8a\n*/\n
Run Code Online (Sandbox Code Playgroud)\n


Tod*_*ier 5

来源中的评论清楚地说明了差异.为什么这些信息不是通过XML文档注释提出的,这对我来说是一个谜.

EscapeUriString:

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

EscapeDataString:

此方法将转义任何非保留字符的字符,包括百分号.

所以区别在于它们如何处理保留字符.EscapeDataString逃避他们; EscapeUriString才不是.

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

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

两种方法都转义既不保留也不保留的字符.

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