如何让Uri.EscapeDataString符合RFC 3986

时间:2022-05-19 00:07:59

The Uri class defaults to RFC 2396. For OpenID and OAuth, I need Uri escaping consistent with RFC 3986.

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

From the System.Uri class documentation:

从System.Uri类文档:

By default, any reserved characters in the URI are escaped in accordance with RFC 2396. This behavior changes if International Resource Identifiers or International Domain Name parsing is enabled in which case reserved characters in the URI are escaped in accordance with RFC 3986 and RFC 3987.

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

The documentation also states that activating this IRI mode and thus the RFC 3986 behavior means adding a uri section element to machine.config and this to your app/web.config file:

该文档还指出激活此IRI模式以及RFC 3986行为意味着将uri节元素添加到machine.config并将其添加到app / web.config文件中:

<configuration>
  <uri>
  <idn enabled="All" />
  <iriParsing enabled="true" />
  </uri>
</configuration>

But whether this is present in the .config file or not, I'm getting the same (non-3986) escaping behavior for a .NET 3.5 SP1 app. What else do I need to do to get Uri.EscapeDataString to use the RFC 3986 rules? (specifically, to escape the reserved characters as defined in that RFC)

但是,无论是否存在于.config文件中,我都会获得与.NET 3.5 SP1应用程序相同的(非3986)转义行为。还有什么办法让Uri.EscapeDataString使用RFC 3986规则? (具体来说,要转义RFC中定义的保留字符)

5 个解决方案

#1


32  

Having not been able to get Uri.EscapeDataString to take on RFC 3986 behavior, I wrote my own RFC 3986 compliant escaping method. It leverages Uri.EscapeDataString, and then 'upgrades' the escaping to RFC 3986 compliance.

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

#2


3  

This has actually been fixed in .NET 4.5 to work by default, see here.

这实际上已在.NET 4.5中修复,默认情况下可以正常工作,请参见此处。

I just created a new library called PUrify (after running into this issue) which will handle getting this to work for .NET pre 4.5 (works for 3.5) and Mono through a variation of the approach in this post. PUrify doesn't change EscapeDataString but it does let you have Uris with reserved chars which will not be escaped.

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

#3


0  

What version of the framework are you using? It looks like a lot of these changes were made in the (from MSDN) ".NET Framework 3.5. 3.0 SP1, and 2.0 SP1" timeframe.

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

#4


0  

I realize this question and answers are a few years old, but I thought I would share my finding when I had trouble getting compliance under .Net 4.5.

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

If your code is running under asp.net, just setting the project to target 4.5 and running on a machine with 4.5 or later, you may still get 4.0 behavior. You need to ensure <httpRuntime targetFramework="4.5" /> is set in the web.config.

如果您的代码在asp.net下运行,只需将项目设置为目标4.5并在4.5或更高版本的计算机上运行,​​您可能仍会获得4.0行为。您需要确保在web.config中设置

From this blog article on msdn,

来自这篇关于msdn的博客文章,

If there is no <httpRuntime targetFramework> attribute present in Web.config, we assume that the application wanted 4.0 quirks behavior.

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

#5


-1  

I could not find a better answer (either 100% framework or 100% reimplementation), so I've created this abomination. Seems to be working with OAuth.

我找不到更好的答案(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();
    }
}

#1


32  

Having not been able to get Uri.EscapeDataString to take on RFC 3986 behavior, I wrote my own RFC 3986 compliant escaping method. It leverages Uri.EscapeDataString, and then 'upgrades' the escaping to RFC 3986 compliance.

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

#2


3  

This has actually been fixed in .NET 4.5 to work by default, see here.

这实际上已在.NET 4.5中修复,默认情况下可以正常工作,请参见此处。

I just created a new library called PUrify (after running into this issue) which will handle getting this to work for .NET pre 4.5 (works for 3.5) and Mono through a variation of the approach in this post. PUrify doesn't change EscapeDataString but it does let you have Uris with reserved chars which will not be escaped.

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

#3


0  

What version of the framework are you using? It looks like a lot of these changes were made in the (from MSDN) ".NET Framework 3.5. 3.0 SP1, and 2.0 SP1" timeframe.

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

#4


0  

I realize this question and answers are a few years old, but I thought I would share my finding when I had trouble getting compliance under .Net 4.5.

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

If your code is running under asp.net, just setting the project to target 4.5 and running on a machine with 4.5 or later, you may still get 4.0 behavior. You need to ensure <httpRuntime targetFramework="4.5" /> is set in the web.config.

如果您的代码在asp.net下运行,只需将项目设置为目标4.5并在4.5或更高版本的计算机上运行,​​您可能仍会获得4.0行为。您需要确保在web.config中设置

From this blog article on msdn,

来自这篇关于msdn的博客文章,

If there is no <httpRuntime targetFramework> attribute present in Web.config, we assume that the application wanted 4.0 quirks behavior.

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

#5


-1  

I could not find a better answer (either 100% framework or 100% reimplementation), so I've created this abomination. Seems to be working with OAuth.

我找不到更好的答案(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();
    }
}