31

Uri 类默认为 RFC 2396。对于 OpenID 和 OAuth,我需要 Uri 转义与 RFC 3986 一致。

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 中定义的保留字符)

4

5 回答 5

38

由于无法让 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();
}
于 2009-05-12T15:45:50.750 回答
4

这实际上已在 .NET 4.5 中修复为默认工作,请参见此处

我刚刚创建了一个名为PURify的新库(在遇到此问题后),它将通过本文中的方法的变体处理使其适用于 .NET pre 4.5(适用于 3.5)和Mono。Purify 不会更改 EscapeDataString 但它确实让您拥有带有保留字符的 Uris,这些字符不会被转义。

于 2013-12-28T17:59:25.520 回答
2

我意识到这个问题和答案已经有几年的历史了,但是当我在 .Net 4.5 下难以获得合规性时,我想我会分享我的发现。

如果您的代码在asp.net 下运行,只需将项目设置为目标4.5 并在4.5 或更高版本的机器上运行,您可能仍然会得到4.0 的行为。您需要确保<httpRuntime targetFramework="4.5" />在 web.config 中进行了设置。

这篇关于 msdn 的博客文章

如果 Web.config 中没有<httpRuntime targetFramework>属性,我们假设应用程序需要 4.0 怪癖行为。

于 2016-12-09T20:08:30.187 回答
0

您使用的是什么版本的框架?看起来很多这些更改是在(来自 MSDN)“.NET Framework 3.5. 3.0 SP1 和 2.0 SP1”时间范围内进行的。

于 2009-05-11T04:04:42.017 回答
0

我找不到更好的答案(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();
    }
}
于 2012-03-21T14:20:54.070 回答