如何在不使用System.Web的情况下进行UrlEncode?

318

我正在尝试编写一个Windows客户端应用程序,调用网站上的数据。为了使安装最小化,我尝试仅使用.NET Framework客户端配置文件中的dll。问题是我需要对一些参数进行UrlEncode,有没有一种简单的方法可以做到这一点,而不需要导入System.Web.dll,因为它不是客户端配置文件的一部分?


你能展示一下你是如何调用网站的吗?也许有些地方可以改进。 - Darin Dimitrov
出于好奇,如果不使用System.Web,你如何称呼一个用于数据的网站? - Patrick McDonald
@Patrick,他可能正在使用WebRequestWebClient。这就是我问及这段特定代码的原因,因为有些事情可以正确地对数据进行URL编码。 - Darin Dimitrov
1
我正在使用 System.Net.WebRequest 对象。然后我调用 GetRequestStream 并将我的 Post 参数写入流中。我还将 ContentType 设置为 "application/x-www-form-urlencoded"。 - Martin Brown
1
当然,如果我执行GET请求并将参数附加到URL上,这同样适用。 - Martin Brown
9个回答

324

System.Uri.EscapeDataString() 在处理某些字符时可能会出现问题,对我来说是字符串中的数字或井号“#”。

如果这对你也是一个问题,请尝试使用:

System.Uri.EscapeDataString() //Works excellent with individual values

这是一个SO问题的答案,解释了EscapeUriString和EscapeDataString之间的区别:

什么是EscapeUriString和EscapeDataString之间的区别?

并推荐在任何方面都使用Uri.EscapeDataString()


1
错误的做法:http://blogs.msdn.com/b/yangxind/archive/2006/11/09/don-t-use-net-system-uri-unescapedatastring-in-url-decoding.aspx。如果使用此方法进行URL解码,加号会出现问题,因为它们不会被解码。 - Chris Weber
7
那篇博客文章有点旧了,我刚刚对完整URL进行了“URI编码”,所有空格都变成了%20,所以我想他们已经修复了这个问题。我正在使用.NET 4.5。 - Rodi
EscapeDataString也不支持准备数据进行POST操作的非常长的字符串。https://dev59.com/9mw15IYBdhLWcg3wT5_2 - Bron Davies
Uri.EscapeUriString确实存在很多问题,不应该使用它,因为它试图做一些(转义完整的URI)实际上是不可能一致地完成的。请参阅此答案以获取详细说明。 - Livven
还有一个空格字符。 - Waqas Shabbir

262

在 .Net 4.5+ 中请使用WebUtility

这里只是为了格式化而提交这个答案。

没有找到任何好的例子来比较它们,所以:

string testString = "http://test# space 123/text?var=val&another=two";
Console.WriteLine("UrlEncode:         " + System.Web.HttpUtility.UrlEncode(testString));
Console.WriteLine("EscapeUriString:   " + Uri.EscapeUriString(testString));
Console.WriteLine("EscapeDataString:  " + Uri.EscapeDataString(testString));
Console.WriteLine("EscapeDataReplace: " + Uri.EscapeDataString(testString).Replace("%20", "+"));

Console.WriteLine("HtmlEncode:        " + System.Web.HttpUtility.HtmlEncode(testString));
Console.WriteLine("UrlPathEncode:     " + System.Web.HttpUtility.UrlPathEncode(testString));

//.Net 4.0+
Console.WriteLine("WebUtility.HtmlEncode: " + WebUtility.HtmlEncode(testString));
//.Net 4.5+
Console.WriteLine("WebUtility.UrlEncode:  " + WebUtility.UrlEncode(testString));

输出:

UrlEncode:             http%3a%2f%2ftest%23+space+123%2ftext%3fvar%3dval%26another%3dtwo
EscapeUriString:       http://test#%20space%20123/text?var=val&another=two
EscapeDataString:      http%3A%2F%2Ftest%23%20space%20123%2Ftext%3Fvar%3Dval%26another%3Dtwo
EscapeDataReplace:     http%3A%2F%2Ftest%23+space+123%2Ftext%3Fvar%3Dval%26another%3Dtwo

HtmlEncode:            http://test# space 123/text?var=val&another=two
UrlPathEncode:         http://test#%20space%20123/text?var=val&another=two

//.Net 4.0+
WebUtility.HtmlEncode: http://test# space 123/text?var=val&another=two
//.Net 4.5+
WebUtility.UrlEncode:  http%3A%2F%2Ftest%23+space+123%2Ftext%3Fvar%3Dval%26another%3Dtwo
在 .Net 4.5+ 中,请使用 WebUtility.UrlEncode 这似乎复制了更常见字符的 HttpUtility.UrlEncode(早于 v4.0):
Uri.EscapeDataString(testString).Replace("%20", "+").Replace("'", "%27").Replace("~", "%7E")
注意: EscapeUriString 会保留有效的 URI 字符串,这会导致它尽可能使用多个纯文本字符。
请参见此答案以查看各种编码的表格比较:
https://dev59.com/FnRB5IYBdhLWcg3wl4Sc#11236038 换行符HttpUtility.HtmlEncode 外,所有列在此处的方法都将把 "\n\r" 转换为 %0a%0d%0A%0D 请随意编辑并向我的测试字符串添加新字符,或将它们留在评论中,我会编辑它。

在我的情况下,我不得不使用EscapeDataString而不是EscapeUriString,因为我们对回车和换行进行了编码,这需要EscapeDataString执行更积极的转义。 - David O'Meara
1
如果您想要更多的示例,可以提供自己的测试用例。这是一个运行示例和其他编码方法的样本,显示了它们之间的差异:https://dotnetfiddle.net/12IFw1 - Maslow
3
WebUtility.UrlEncode() 和 WebUtility.UrlDecode() 是 4.5+ 版本才有的,它们在 4.0 版本中并不存在。 - Derek Kalweit
MSDN上说:“通用Windows平台:自4.5版本开始提供,.NET Framework:自4.0版本开始提供”… - Thymine

54

这个和EscapeDataString有什么区别吗? - Martin Brown
3
你想要使用EscapeUriString。EscapeUriString将尝试对整个URL进行编码(包括http://部分),而EscapeUriString会理解哪些部分实际上应该被编码。 - Matthew Manela
1
我明白了,所以在这种情况下,我可能会想要使用EscapeDataString,因为我可能想将URL作为GET参数传递。在这种情况下,我正在附加到一个URL上。 - Martin Brown
5
我很确定你的Oct1的评论应该写成EscapeDataString会尝试编码... - Maslow
不要使用Uri.EscapeUriString。它并不“理解”哪些部分应该被编码,它只是一种误导性的尝试去做一些(转义完整的URI),这实际上是不可能始终如一地做到的。请参见此答案以获取详细说明。 - Livven

23

这里的答案非常好,但对我来说仍然不够。

我编写了一个小循环,比较了从0到255的所有字符的Uri.EscapeUriStringUri.EscapeDataString

注意:两个函数都具有内置智能,即对于大于0x80的字符,先进行UTF-8编码,然后进行百分号编码。

以下是结果:

******* Different *******

'#' -> Uri "#" Data "%23"
'$' -> Uri "$" Data "%24"
'&' -> Uri "&" Data "%26"
'+' -> Uri "+" Data "%2B"
',' -> Uri "," Data "%2C"
'/' -> Uri "/" Data "%2F"
':' -> Uri ":" Data "%3A"
';' -> Uri ";" Data "%3B"
'=' -> Uri "=" Data "%3D"
'?' -> Uri "?" Data "%3F"
'@' -> Uri "@" Data "%40"


******* Not escaped *******

'!' -> Uri "!" Data "!"
''' -> Uri "'" Data "'"
'(' -> Uri "(" Data "("
')' -> Uri ")" Data ")"
'*' -> Uri "*" Data "*"
'-' -> Uri "-" Data "-"
'.' -> Uri "." Data "."
'_' -> Uri "_" Data "_"
'~' -> Uri "~" Data "~"

'0' -> Uri "0" Data "0"
.....
'9' -> Uri "9" Data "9"

'A' -> Uri "A" Data "A"
......
'Z' -> Uri "Z" Data "Z"

'a' -> Uri "a" Data "a"
.....
'z' -> Uri "z" Data "z"

******* UTF 8 *******

.....
'Ò' -> Uri "%C3%92" Data "%C3%92"
'Ó' -> Uri "%C3%93" Data "%C3%93"
'Ô' -> Uri "%C3%94" Data "%C3%94"
'Õ' -> Uri "%C3%95" Data "%C3%95"
'Ö' -> Uri "%C3%96" Data "%C3%96"
.....

EscapeUriString 用于对URL进行编码,而 EscapeDataString则用于对Cookie内容进行编码,因为Cookie数据不得包含保留字符'='';'


这里有很好的分析和细致的解释,非常有帮助。如果有人拥有或知道性能基准测试(比较所有三种方法),那也很不错。 - Shaun Wilson
这是一篇很好的分析,结论是你不应该使用Uri.EscapeUriString,因为无法一致地转义完整的URI。请参见此答案以获取详细说明。 - Livven

17

在客户端框架(System.dll)中,存在一个可用的客户端配置文件版本(System.Net.WebUtility类)。以下是 MSDN 链接:

WebUtility


我想指出该类的帮助页面明确指出:“在处理 Web 请求时,提供了编码和解码 URL 的方法。”因此可能只是它们没有很好地命名这些方法。 - James White
好主意,说起来,为什么不给兄弟点个赞;)这个踩票已经困扰我两年了!开玩笑...但说实话,这可能就是我发帖的原因,很不幸,我因为 Microsoft 文档中的错误而受到声誉打击... - Sprague
11
看起来UrlEncode和UrlDecode只在.Net的4.5版本中才被添加到WebUtility中。 - Martin Brown

8
以下是一个使用 application/x-www-form-urlencoded 内容类型正确编码参数的发送POST请求的示例:
using (var client = new WebClient())
{
    var values = new NameValueCollection
    {
        { "param1", "value1" },
        { "param2", "value2" },
    };
    var result = client.UploadValues("http://foo.com", values);
}

0

我被迫在一些项目中使用.NET 4.0,因此,WebUtilityHttpUtility都不包含这些内容。我使用了Uri.EscapeDataString()方法,它的效果非常好,但我不喜欢它没有一次性编码所有标准特殊字符(意味着! "#$%&'()*+,-./:;<=>?@[\]^_`{|}~)。我更多地处理Visual Basic而不是C#,所以我不确定转换以下内容需要什么,但它对我的基本需求非常有效。

我不会处理任何UTF-8格式的字符串,因为它仅用于一些非常基本的文本操作,并且到目前为止已经为我服务得很好。它不会以任何方式解析出换行符(我要操作的文本不会有它们),并且您必须首先处理%符号,以防止它破坏其余符号的编码。有点丑陋,但它有效。

Function EncodeURL(ByVal DecodedString As String) As String

  DecodedString = Replace(DecodedString, "%", "%25", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, " ", "%20", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "!", "%21", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, """", "%22", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "#", "%23", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "$", "%24", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "&", "%26", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "'", "%27", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "(", "%28", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, ")", "%29", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "*", "%2A", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "+", "%2B", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, ",", "%2C", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "-", "%2D", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, ".", "%2E", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "/", "%2F", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, ":", "%3A", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, ";", "%3B", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "<", "%3C", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "=", "%3D", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, ">", "%3E", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "?", "%3F", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "@", "%40", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "[", "%5B", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "\", "%5C", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "]", "%5D", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "^", "%5E", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "_", "%5F", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "`", "%60", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "{", "%7B", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "|", "%7C", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "}", "%7D", 1, vbTextCompare)
  DecodedString = Replace(DecodedString, "~", "%7E", 1, vbTextCompare)

  EncodeURL = DecodedString

End Function

输入:

! "#$%&'()*+,-./:;<=>?@[]^_`{|}~

输出

%21%20%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E

你可以使用Dictionary<char, string>来定义每个要替换的字符到替换文本的映射(例如,'!'"21")来使这段代码更易于管理和高效。然后枚举DecodedString并将替换文本(如果在Dictionary<,>中找到输入char)或者输入char附加到StringBuilder中。另外,由于字符被编码为十六进制ASCII值,你也可以使用HashSet<char>switch或简单的范围(例如,0x23 ≤ c ≤ 0x2C等)来定义要替换的char,然后发出(byte) c - Lance U. Matthews
哦!我喜欢这个想法!这与在Excel中处理大范围的单元格非常相似-将其放入数组中可以让您更快地读取数据。 - k1dfr0std
另外,我必须检查一下您如何编辑帖子以允许代码样式文本中的所有符号和正确的VB.NET语言格式。非常感谢您的帮助 - 我未来的帖子将有更好的格式! - k1dfr0std
1
非常好。如果你想知道我是如何在内联代码中包含反引号的,可以参考“如何格式化我的代码块?”这个答案的结尾部分,其中记录了保留反引号的方法。我使用了lang-vbnet语言提示来指定VB.NET语言,但是还有其他别名可以使用(例如lang-vb)以及标签名称(例如vb.net),如果它有相关的语言代码。请参见“什么是语法高亮,它是如何工作的?”这个答案。 - Lance U. Matthews

0

-3
System.Net.WebUtility.HtmlDecode

WebUtility类提供了在处理Web请求时对URL进行编码和解码的方法。它与HttpUtility执行相同的操作,但不属于System.Web命名空间。 - Alexandru Aliu
3
这是错误的,因为它使用了 HtmlDecode 而非 UrlEncode,与问题所要求的不同。即使使用 HtmlEncode 也是错误的,因为 HTML 编码与 URL 编码是不同的。 - Martin Brown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接