Как заставить Uri.EscapeDataString соответствовать RFC 3986 - PullRequest
30 голосов
/ 11 мая 2009

Класс 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 или нет, я получаю такое же (не 3986) экранирующее поведение для приложения .NET 3.5 SP1. Что еще мне нужно сделать, чтобы Uri.EscapeDataString использовал правила RFC 3986? (в частности, для экранирования зарезервированных символов, как определено в этом RFC)

Ответы [ 5 ]

35 голосов
/ 12 мая 2009

Не имея возможности заставить 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();
}
4 голосов
/ 28 декабря 2013

Это было исправлено в .NET 4.5 для работы по умолчанию, см. здесь .

Я только что создал новую библиотеку под названием PUrify (после того, как столкнулся с этой проблемой), которая будет обрабатывать его для работы в .NET pre 4.5 (работает для 3.5) и Mono посредством изменения подхода в этот пост . PUrify не изменяет EscapeDataString, но позволяет вам иметь Uris с зарезервированными символами, которые не будут экранированы.

2 голосов
/ 09 декабря 2016

Я понимаю, что этому вопросу и ответам несколько лет, но я подумал, что поделюсь своим выводом, когда у меня возникли проблемы с соблюдением по .Net 4.5 .

Если ваш код работает на asp.net, просто установив проект на целевой уровень 4.5 и запустив его на компьютере с 4.5 или более поздней версией, вы все равно можете получить поведение 4.0. Вы должны убедиться, что <httpRuntime targetFramework="4.5" /> установлен в web.config.

Из этой статьи блога на MSDN ,

Если атрибут <httpRuntime targetFramework> отсутствует в Web.config, мы предполагаем, что приложение хотело 4.0 причудливого поведения.

0 голосов
/ 21 марта 2012

Я не смог найти лучшего ответа (ни 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();
    }
}
0 голосов
/ 11 мая 2009

Какую версию фреймворка вы используете? Похоже, что многие из этих изменений были сделаны в ( от MSDN ) ".NET Framework 3.5. 3.0 SP1 и 2.0 SP1".

...