如何让Uri.EscapeDataString符合RFC 3986

时间:2009-05-11 01:56:06

标签: .net openid oauth escaping uri

Uri类默认为RFC 2396.对于OpenID和OAuth,我需要符合RFC 3986的Uri转义。

来自System.Uri class documentation

  

默认情况下,URI中的任何保留字符都会根据RFC 2396进行转义。如果启用了国际资源标识符或国际域名解析,则此行为会发生变化,在这种情况下,URI中的保留字符将根据RFC 3986进行转义, RFC 3987。

该文档还指出,激活此IRI模式以及RFC 3986行为意味着将uri section元素添加到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中定义的保留字符)

5 个答案:

答案 0 :(得分:35)

由于无法让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();
}

答案 1 :(得分:4)

默认情况下,这已在.NET 4.5中修复,请参阅here

我刚刚创建了一个名为PUrify的新库(在遇到此问题之后),它将通过此​​方法的变体来处理.NET 4.5(适用于3.5)和Mono的工作。 {3}}。 PUrify不会改变EscapeDataString,但它确实让你拥有Uris和保留的字符,不会被转义。

答案 2 :(得分:2)

我意识到这个问题并且答案已经有几年了,但是当我遇到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怪癖行为。

答案 3 :(得分:0)

您使用的是哪种版本的框架?看起来很多这些更改都是在(from MSDN)“.NET Framework 3.5.3.0 SP1和2.0 SP1”时间范围内进行的。

答案 4 :(得分: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();
    }
}