Подтвердить что ты не робот

Как заставить Uri.EscapeDataString соответствовать RFC 3986

По умолчанию для класса 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)

4b9b3361

Ответ 1

Не удалось заставить Uri.EscapeDataString принять поведение RFC 3986, я написал собственный метод escaping, совместимый с 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

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

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

Ответ 3

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

Ответ 4

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

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

От эту статью в блоге по msdn,

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

Ответ 5

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