如何让 Uri.EscapeDataString 符合 RFC 3986
Posted
技术标签:
【中文标题】如何让 Uri.EscapeDataString 符合 RFC 3986【英文标题】:How to get Uri.EscapeDataString to comply with RFC 3986 【发布时间】:2010-10-25 04:38:12 【问题描述】:Uri 类默认为 RFC 2396。对于 OpenID 和 OAuth,我需要符合 RFC 3986 的 Uri 转义。
来自System.Uri class documentation:
默认情况下,URI 中的任何保留字符都会根据 RFC 2396 进行转义。如果启用国际资源标识符或国际域名解析,则此行为会发生变化,在这种情况下,URI 中的保留字符会根据 RFC 3986 进行转义,并且RFC 3987。
该文档还指出,激活此 IRI 模式以及因此 RFC 3986 行为意味着将 uri 部分元素添加到 machine.config 并将其添加到您的 app/web.config 文件:
<configuration>
<uri>
<idn enabled="All" />
<iriParsing enabled="true" />
</uri>
</configuration>
但是无论这是否存在于 .config 文件中,对于 .NET 3.5 SP1 应用程序,我都会得到相同的(非 3986)转义行为。 我还需要做什么才能让Uri.EscapeDataString
使用 RFC 3986 规则?(特别是转义该 RFC 中定义的保留字符)
【问题讨论】:
我在语法示例中的错字处添加了一些社区内容。 我在内部将此作为 .NET Framework 的错误提交(我为 MSFT 工作)。他们承认这是一个文档错误,因为这个配置设置确实不会使 Uri 类在转义方面表现得像 RFC 3986。 【参考方案1】:由于无法让 Uri.EscapeDataString 承担 RFC 3986 行为,我编写了自己的符合 RFC 3986 的转义方法。它利用 Uri.EscapeDataString,然后将转义“升级”到 RFC 3986 合规性。
/// <summary>
/// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986.
/// </summary>
private static readonly string[] UriRfc3986CharsToEscape = new[] "!", "*", "'", "(", ")" ;
/// <summary>
/// Escapes a string according to the URI data string rules given in RFC 3986.
/// </summary>
/// <param name="value">The value to escape.</param>
/// <returns>The escaped value.</returns>
/// <remarks>
/// The <see cref="Uri.EscapeDataString"/> method is <i>supposed</i> to take on
/// RFC 3986 behavior if certain elements are present in a .config file. Even if this
/// actually worked (which in my experiments it <i>doesn't</i>), we can't rely on every
/// host actually having this configuration element present.
/// </remarks>
internal static string EscapeUriDataStringRfc3986(string value)
// Start with RFC 2396 escaping by calling the .NET method to do the work.
// This MAY sometimes exhibit RFC 3986 behavior (according to the documentation).
// If it does, the escaping we do that follows it will be a no-op since the
// characters we search for to replace can't possibly exist in the string.
StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));
// Upgrade the escaping to RFC 3986, if necessary.
for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
// Return the fully-RFC3986-escaped string.
return escaped.ToString();
【讨论】:
你知道 .net 4.5 是否最终解决了这个问题吗? msdn.microsoft.com/en-us/library/hh367887(v=VS.110).aspx 感谢角色列表。我最终自己做了一个 1 行:Regex.Replace(Uri.EscapeDataString(s), "[\!*\'\(\)]", Function(m) Uri.HexEscape(Convert.ToChar(m.值(0).ToString()))) @AndrewArnott,谢谢。这适用于我的 .Net 3.5 应用程序。【参考方案2】:这实际上已在 .NET 4.5 中修复为默认工作,请参阅here。
我刚刚创建了一个名为 PUrify 的新库(在遇到此问题后),它将通过此post 中的方法变体处理使其适用于 .NET pre 4.5(适用于 3.5)和 Mono . PURify 不会改变 EscapeDataString 但它确实让你拥有不会被转义的保留字符的 Uris。
【讨论】:
【参考方案3】:我知道这个问题和答案已经有几年的历史了,但是当我无法获得compliance under .Net 4.5 时,我想我会分享我的发现。
如果您的代码在 asp.net 下运行,只需将项目设置为目标 4.5 并在具有 4.5 或更高版本的机器上运行,您可能仍然会得到 4.0 的行为。您需要确保在 web.config 中设置了<httpRuntime targetFramework="4.5" />
。
来自this blog article on msdn,
如果没有
<httpRuntime targetFramework>
属性存在于 Web.config,我们假设应用程序需要 4.0 的怪癖行为。
【讨论】:
【参考方案4】:您使用的是哪个版本的框架?看起来很多这些更改是在 (from MSDN)“.NET Framework 3.5.3.0 SP1 和 2.0 SP1”时间范围内进行的。
【讨论】:
我已经补充说我正在使用 .NET 3.5 SP1 来回答我的问题。我很高兴地注意到,您链接的 MSDN 文章也与其自身严重不一致,当区分大小写很重要时,无效的 XML、Uri 和 uri 可以互换使用,而当值应该是“全部”时,我找不到更好的答案(100% 框架或 100% 重新实现),所以我创造了这个可憎的东西。似乎正在使用 OAuth。
class al_RFC3986
public static string Encode(string s)
StringBuilder sb = new StringBuilder(s.Length*2);//VERY rough estimate
byte[] arr = Encoding.UTF8.GetBytes(s);
for (int i = 0; i < arr.Length; i++)
byte c = arr[i];
if(c >= 0x41 && c <=0x5A)//alpha
sb.Append((char)c);
else if(c >= 0x61 && c <=0x7A)//ALPHA
sb.Append((char)c);
else if(c >= 0x30 && c <=0x39)//123456789
sb.Append((char)c);
else if (c == '-' || c == '.' || c == '_' || c == '~')
sb.Append((char)c);
else
sb.Append('%');
sb.Append(Convert.ToString(c, 16).ToUpper());
return sb.ToString();
【讨论】:
因为答案是糟糕的 imo。以上是关于如何让 Uri.EscapeDataString 符合 RFC 3986的主要内容,如果未能解决你的问题,请参考以下文章
Uri.EscapeDataString() - 无效的 URI:Uri 字符串太长
通过“HttpUtility.UrlEncode”和“Uri.EscapeDataString”在 GET API 请求中编码撇号会出错
EscapeUriString 和 EscapeDataString 有啥区别?
Owin的URL编码怎么搞?以前都是HttpUtility.UrlEncode之类的,现在连system.web都没了,肿么办?