В чем разница между EscapeUriString и EscapeDataString? - PullRequest
168 голосов
/ 09 декабря 2010

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

Ответы [ 5 ]

184 голосов
/ 10 декабря 2015

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

Нет веских причин использовать 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-конструктора, и если он недействителен, то вызов Uri.EscapeUriString также не является волшебным решением. На самом деле, это будет работать во многих, если не в большинстве случаев, но отнюдь не надежно.

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

99 голосов
/ 09 декабря 2010

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

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

54 голосов
/ 14 января 2015

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

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

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

Теперь рассмотрим запрос Google на «happy c ++»:

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

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

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

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

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

5 голосов
/ 04 декабря 2017

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

EscapeUriString:

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

EscapeDataString:

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

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

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

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

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

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

2 голосов
/ 02 сентября 2018

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

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
*/
...