如何使 Uri.EscapeDataString 符合 RFC 3986
题
Uri 类默认为 RFC 2396。对于 OpenID 和 OAuth,我需要与 RFC 3986 一致的 Uri 转义。
来自 System.Uri 类文档:
默认情况下,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 中定义的保留字符)
解决方案
具有不能够得到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 。
如果你的代码是在asp.net运行时,只需设置项目的目标4.5和4.5或更高版本的机器上运行,你还可以得到4.0的行为。您需要确保<httpRuntime targetFramework="4.5" />
在web.config设置。
从 MSDN上这个博客文章,
如果有存在于没有
<httpRuntime targetFramework>
属性 Web.config中,我们假设应用程序想要4.0的怪癖行为。
什么版本的框架,您使用的是?它看起来像很多这些变化从MSDN中(是由)”的.NET Framework 3.5。3.0 SP1,和2.0 SP1" 时间表。
我无法找到一个更好的答案(无论是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();
}
}