В чем разница между EscapeUriString и EscapeDataString?

Если только иметь дело с URL-кодировкой, я должен использовать EscapeUriString?

6 ответов

Решение

Использование EscapeDataString всегда (для получения дополнительной информации о причинах см . ответ Ливвена ниже)

Редактировать: удалена неработающая ссылка на то, как эти два кода различаются

Я не нашел существующие ответы удовлетворительными, поэтому я решил немного углубиться, чтобы решить эту проблему. Удивительно, но ответ очень прост:

Нет веской причины когда-либо использовать Uri.EscapeUriString , Если вам нужно кодировать строку в процентах, всегда используйте Uri.EscapeDataString ,

Почему это? Согласно документации:

Используйте метод EscapeUriString для подготовки неэкранированной строки URI в качестве параметра для конструктора Uri.

Это на самом деле не имеет смысла. Согласно RFC 2396:

URI всегда находится в "экранированной" форме, поскольку экранирование или удаление из завершенного URI может изменить его семантику.

В то время как цитируемый RFC был устаревшим в RFC 3986, точка зрения остается в силе. Давайте проверим это, посмотрев на некоторые конкретные примеры:

  1. У вас есть простой URI, например:

    http://example.org/
    

    Uri.EscapeUriString не изменится.

  2. Вы решаете вручную редактировать строку запроса без учета экранирования:

    http://example.org/?key=two words
    

    Uri.EscapeUriString (правильно) покинет место для вас:

    http://example.org/?key=two%20words
    
  3. Вы решили вручную отредактировать строку запроса:

    http://example.org/?parameter=father&son
    

    Однако эта строка не изменяется Uri.EscapeUriString, поскольку он предполагает, что амперсанд означает начало другой пары ключ-значение. Это может или не может быть то, что вы хотели.

  4. Вы решаете, что на самом деле хотите key параметр, который будет father&son Таким образом, вы исправляете предыдущий URL вручную, экранируя амперсанд:

    http://example.org/?parameter=father%26son
    

    Тем не мение, Uri.EscapeUriString также будет экранировать символ процента, что приведет к двойной кодировке:

    http://example.org/?parameter=father%2526son
    

Как видите, используя Uri.EscapeUriString по своему прямому назначению делает невозможным использование & как часть ключа или значения в строке запроса, а не как разделитель между несколькими парами ключ-значение.

Это связано с тем, что при ошибочной попытке сделать его подходящим для экранирования полных URI он игнорирует зарезервированные символы и экранирует только те символы, которые не являются ни зарезервированными, ни незарезервированными, что, кстати, противоречит документации. Таким образом, вы не получите что-то вроде http%3A%2F%2Fexample.org%2F, но вы в конечном итоге с проблемами, показанными выше.


В конце концов, если ваш URI действителен, его не нужно экранировать, чтобы передать как параметр Uri construtor, а если он недействителен, то вызов Uri.EscapeUriString тоже не волшебное решение. На самом деле, это будет работать во многих, если не в большинстве случаев, но отнюдь не надежно.

Вы всегда должны создавать свои URL-адреса и строки запросов, собирая пары ключ-значение и процентное кодирование, а затем объединяя их с необходимыми разделителями. Ты можешь использовать Uri.EscapeDataString для этого, но не Uri.EscapeUriString, поскольку он не экранирует зарезервированные символы, как упомянуто выше.

Символы плюс (+) могут многое рассказать о разнице между этими методами. В простом URI символ "плюс" означает "пробел". Подумайте о том, чтобы запросить у Google "счастливый кот"

https://www.google.com/?q=happy+cat

Это действительный URI (попробуйте), и EscapeUriString не будет изменять его.

Теперь рассмотрите вопрос о том, чтобы запросить у Google "happy C++":

https://www.google.com/?q=happy+c++

Это действительный URI (попробуйте), но он производит поиск "happy c", потому что два плюса интерпретируются как пробелы. Чтобы исправить это, мы можем передать "счастливый C++" EscapeDataString и вуаля*:

https://www.google.com/?q=happy+c%2B%2B

*) Закодированная строка данных на самом деле "happy%20c%2B%2B"; %20 - шестнадцатеричный символ пробела, а% 2B - шестнадцатеричный символ плюса.

Если вы используете UriBuilder как и должно быть, тогда вам понадобится только EscapeDataString правильно экранировать некоторые компоненты всего вашего URI. @ Ответ Ливвена на этот вопрос еще раз доказывает, что на самом деле нет причин использовать EscapeUriString,

Комментарии в источнике четко указывают на разницу. Почему эта информация не передается в комментариях к документации XML, для меня загадка.

EscapeUriString:

Этот метод будет экранировать любой символ, который не является зарезервированным или незарезервированным символом, включая знаки процента. Обратите внимание, что EscapeUriString также не будет экранировать знак "#".

EscapeDataString:

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

Так что разница в том, как они обрабатывают зарезервированные символы. EscapeDataString избегает их; EscapeUriString не.

Согласно RFC, зарезервированные символы: :/?#[]@!$&'()*+,;=

Для полноты, незарезервированные символы являются буквенно-цифровыми и -._~

Оба метода экранируют символы, которые не являются ни зарезервированными, ни незарезервированными.

Я не согласен с общим представлением о том, что EscapeUriString это зло Я думаю, что метод, который экранирует только недопустимые символы (например, пробелы) и незарезервированные символы, является полезным. Но у него есть причуды в том, как он обрабатывает % персонаж. Символы в процентах (% сопровождаемые 2 шестнадцатеричными цифрами) являются допустимыми в URI. Я думаю EscapeUriString было бы гораздо полезнее, если бы он обнаружил этот шаблон и избежал кодирования % когда это немедленно продолжается 2 шестнадцатеричными цифрами.

Простой пример

var data = "example.com/abc?DEF=あいう\x20えお";

Console.WriteLine(Uri.EscapeUriString(data));
Console.WriteLine(Uri.EscapeDataString(data));
Console.WriteLine(System.Net.WebUtility.UrlEncode(data));
Console.WriteLine(System.Web.HttpUtility.UrlEncode(data));

/*
=>
example.com/abc?DEF=%E3%81%82%E3%81%84%E3%81%86%20%E3%81%88%E3%81%8A
example.com%2Fabc%3FDEF%3D%E3%81%82%E3%81%84%E3%81%86%20%E3%81%88%E3%81%8A
example.com%2Fabc%3FDEF%3D%E3%81%82%E3%81%84%E3%81%86+%E3%81%88%E3%81%8A
example.com%2fabc%3fDEF%3d%e3%81%82%e3%81%84%e3%81%86+%e3%81%88%e3%81%8a
*/

Я использовал зашифрованную строку в качестве параметра URL-адреса (например, http://example.com/Test/myencryptedkey/param2/param3), поэтому ни один из методов шифрования C# не мог предоставить безопасный параметр URL-адреса. В итоге я использовал следующий шаблон:

Во время шифрования: Uri.EscapeDataString(myencryptedkey).Replace('%', '~');

Во время расшифровки: Uri.UnescapeDataString(myencryptedkey.Replace('~', '%'));

Обратите внимание, что во время шифрования замена происходит после EscapeDataString(), тогда как во время дешифрования замена происходит до UnescapeDataString ();

Другие вопросы по тегам