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

196

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

user496949
источник
10
Всегда избегайте использования каждого отдельного значенияUri.EscapeDataString() , как описано в ответе @ Livven. При других подходах система просто не имеет достаточно информации для получения ожидаемого результата для каждого возможного ввода.
Тимо

Ответы:

112

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

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

Jcl
источник
3
Я не уверен, что эта ссылка на самом деле предоставляет больше информации, так как она касается удаления, а не удаления.
Стивен
1
Это в основном та же разница. Если вы на самом деле читаете статью, есть середина таблицы, которая на самом деле ускользает (не исчезает), чтобы показать различия (по сравнению с другими URLEncode).
Jcl
2
Мне все еще не ясно - что, если я не экранирую весь URI, а только его часть (то есть данные для параметра строки запроса)? Я экранирую данные для URI, или EscapeDataString подразумевает что-то совершенно другое?
BrainSlugs83
4
... сделал какое-то тестирование, похоже, что я хочу EscapeDataString для параметра URI. Я тестировал со строкой "I heart C ++", и EscapeUriString не кодировал символы "+", он просто оставил их как есть, EscapeDataString правильно преобразовал их в "% 2B".
BrainSlugs83
7
Это плохой ответ. Вы никогда не должны использовать EscapeUriString, это не имеет никакого смысла. Смотрите ответ Ливвен ниже (и проголосуйте за него).
Брэндон Паддок
245

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

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

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

Livven
источник
4
Вау, спасибо, что наконец-то прояснил этот вопрос. Предыдущие два ответа были не очень полезны.
EverPresent
3
Совершенно верно. EscapeUriString (подобно поведению EscapeUrl по умолчанию в Win32) был создан кем-то, кто не понимал URI или экранировал. Это ошибочная попытка создать что-то, что принимает некорректный URI и иногда превращает его в предполагаемую версию. Но у него нет информации, необходимой для того, чтобы сделать это надежно. Он также часто используется вместо EscapeDataString, что также очень проблематично. Я бы хотел, чтобы EscapeUriString не существовало. Каждое использование это неправильно.
Брэндон Паддок
4
приятно объяснил +1, это намного лучше, чем принятая ссылка только ответ
Эхсан Саджад
1
Этот ответ требует большего внимания. Это правильный способ сделать это. Другие ответы имеют сценарии, в которых они не дают ожидаемых результатов.
Тимо
1
... Конечно encodeURI/ Uri.EscapeUriStringне нужно так часто, как encodeURIComponent/ Uri.EscapeDataString(так как, когда вы опаздываете со слепыми URL-адресами, которые должны использоваться в контексте URI), но это не значит, что ему не место.
Crescent Fresh
56

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

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

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

Теперь рассмотрим запрос Google на «счастливый c ++»:

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

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

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

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

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

Сет
источник
Спасибо. Например, когда у вас есть абсолютная строка URI, которую нужно кодировать "https://www.google.com/?q=happy c++". Похоже, мне нужно вручную разделить на "?", Или есть лучший способ?
Венсвен
Если вы передаете весь URL-адрес в качестве параметра другому URL-адресу, используйте EscapeDataString. Если указанный вами URL-адрес является фактическим, то да, вы хотите просто разделить его ?.
Сет
7

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

EscapeUriString:

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

EscapeDataString:

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

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

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

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

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

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

Тодд менье
источник
1

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

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
*/
Обучение
источник