Яка різниця між URI.escape і CGI.escape?


147

У чому різниця між URI.escapeі від CGI.escapeкого я повинен користуватися?

Відповіді:


124

Були деякі невеликі відмінності, але важливим моментом є те URI.escape, що в Ruby 1.9.2 застаріло, тому використовуйте CGI::escapeабо ERB :: Util.url_encode .

Існує тривала дискусія про ruby-core для зацікавлених, хто також згадує WEBrick :: HTTPUtils.escape і WEBrick :: HTTPUtils.escape_form .


11
Просто для додавання плутанини - я щойно побачив коментар на сайті stackoverflow.com/questions/4967608/…, де хтось згадав, що cgi escape використовує "+" замість% 20 ​​для пробілів, і що це проти "специфікації" ...
Луї Сайерс

18
альтернативою є використання, ERB::Util.url_encodeяке належним чином використовується %20 для пробілів
riffraff

1
@Ernest: Див.: Github.com/ruby/ruby/commit/… (відповідь оновлена)
Марк-Андре Лафортун

4
ruby-doc.org/stdlib-2.0.0/libdoc/uri/rdoc/URI/Escape.html . У рубіні 2.0.0 є модуль URI.escape. Чому його застаріли?
user938363

1
@ user938363 Якщо натиснути джерело шоу там, ви побачите, що воно як і раніше застаріло.
велетенський

229

Яка різниця між сокирою та мечем і яким я повинен користуватися? Ну, це залежить від того, що вам потрібно зробити.

URI.escapeповинен був кодувати рядок (URL) у, так званому, " кодуванні відсотків ".

CGI::escapeпоходить від специфікації CGI , яка описує, як дані повинні кодуватися / декодувати між веб-сервером та програмою.

Тепер скажімо, що вам потрібно уникнути URI у вашому додатку. Це більш конкретний випадок використання. Для цього громада Рубі використовувала URI.escapeроками. Проблема URI.escapeполягала в тому, що він не міг обробити специфікацію RFC-3896.

URI.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog' 
# => "http://google.com/foo?bar=at%23anchor&title=My%20Blog%20&%20Your%20Blog"

URI.escape було позначено як застаріле:

Більше того, поточний код URI.en є простим gsub. Але я думаю, що він повинен розділити URI на компоненти, потім вийти з кожного компонента і, нарешті, приєднатись до них.

Отже, поточний код URI.en вважається шкідливим та застарілим. Це буде видалено або змінить поведінку кардинально.

Що таке заміна на даний момент?

Як я вже говорив вище, поточний URI.encode неправильний на рівні специфікації. Тож ми не забезпечимо точну заміну. Заміна буде залежати від випадку використання.

https://bugs.ruby-lang.org/isissue/4167

На жаль, в документах немає жодного слова про це, єдиний спосіб дізнатися про нього - це перевірити джерело або запустити скрипт із попередженнями на рівні докладної (-wW2 ) (або використовувати якийсь google-fu).

Деякі пропонують використовувати CGI::Escapeдля параметрів запиту, оскільки ви не змогли уникнути цілого URI:

CGI::escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http%3A%2F%2Fgoogle.com%2Ffoo%3Fbar%3Dat%23anchor%26title%3DMy+Blog+%26+Your+Blog"

CGI::escapeслід використовувати лише для параметрів запиту, але результати знову будуть відповідати специфікації. Насправді найпоширенішим випадком використання є втеча даних даних, наприклад, під час надсиланняapplication/x-www-form-urlencoded запиту POST.

Крім того, згадане WEBrick::HTTPUtils.escapeне суттєве поліпшення (знову-таки це просто простий gsubваріант, що є, IMO, навіть гірший варіант, ніж URI.escape):

WEBrick::HTTPUtils.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http://google.com/foo?bar=at%23anchor&title=My%20Blog%20&%20Your%20Blog" 

Найближчим до специфікації, здається, є Адресний дорогоцінний камінь:

require 'addressable/uri'
Addressable::URI.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http://google.com/foo?bar=at#anchor&title=My%20Blog%20&%20Your%20Blog"

Зауважте, що на відміну від усіх попередніх параметрів, адреса не виходить #, і це очікувана поведінка. ви хочете зберегти #хеш у шляху URI, але не у запиті URI.

Єдина проблема, що залишилася - це те, що ми не уникнули належних параметрів запиту, що підводить нас до висновку: ми не повинні використовувати єдиний метод для всього URI, оскільки немає ідеального рішення (поки що). Як бачите, &не вийшли з "Мій блог і ваш блог". Нам потрібно використовувати іншу форму втечі для парамерів запитів, де користувачі можуть розміщувати різні символи, які мають особливе значення в URL-адресах. Введіть кодування URL-адреси. Кодування URL-адреси слід використовувати для кожного "підозрілого" значення запиту, подібного до того, що ERB::Util.url_encodeробить:

ERB::Util.url_encode "My Blod & Your Blog"
# => "My%20Blod%20%26%20Your%20Blog""

Це здорово, але ми вже вимагали адреси:

uri = Addressable::URI.parse("http://www.go.com/foo")
# => #<Addressable::URI:0x186feb0 URI:http://www.go.com/foo>
uri.query_values = {title: "My Blog & Your Blog"}
uri.normalize.to_s
# => "http://www.go.com/foo?title=My%20Blog%20%26%20Your%20Blog"

Висновок:

  • Не використовуйте URI.escapeабо подібне
  • Використовуйте, CGI::escapeякщо вам потрібна лише форма втечі
  • Якщо вам потрібно працювати з URI, використовуйте Addressable, він пропонує кодування URL-адрес, кодування форми та нормалізує URL-адреси.
  • Якщо це проект Rails, перевірте " Як я можу отримати URL-адресу в рядку Rails? "

Дякую за інформацію. Це впевнено позбулося деяких попереджень про тестування мотикою. Граб і мотика дивляться внизу.
Дуглас Г. Аллен

Чудове пояснення @Ernest, але проблема в цьому полягає в тому, що він не працює для зовнішніх URL-адрес, які я не намагаюся створити (і не маю над цим контролю). наприклад, сканери, які читають URL-адреси з веб-сторінки, а потім намагаються отримати доступ до тих URL-адрес (які потрібно закодувати перед доступом).
amit_saxena

@amit_saxena, якщо ви можете дозволити собі Addressableодин із своїх дорогоцінних каменів, ви можете спершу проаналізувати URL-адресу, fi rubydoc.info/gems/addressable/Addressable/URI.heuristic_parse
Ернест

Цікаво! Але знову ж таки, я не можу отримати хеш параметрів з оригінальної URL-адреси, використовуючи це, яке потім кодую, як ви описуєте. Потік у моєму випадку такий: я отримую зовнішні URL-адреси з якогось каналу -> який мені потім потрібно кодувати -> Перейти до http-клієнта для отримання вмісту. Тепер, якщо я не кодую зовнішні URL-адреси належним чином, HTTP-клієнти, що базуються на рубіні, не працюють з помилками URI.
amit_saxena

@amit_saxena метод розбору поверне екземпляр Addressable:URL , потім ви можете викликати всі методи екземпляра на ньому, можливо, один з них отримає бажані результати: rubydoc.info/gems/addressable/Addressable/URI
Ернест


6

CGI::escapeдобре підходить для виділення текстового сегменту, щоб їх можна було використовувати в параметрах запиту URL (рядки після '?'). Наприклад, якщо ви хочете, щоб у URL-адресі був параметр, який містить символи косою рисою, ви спочатку перейдете до цього рядка CGI :: і вставте його в URL-адресу.

Однак у Rails ви, ймовірно, не будете використовувати його безпосередньо. Зазвичай ви використовуєте hash.to_param, що будете використовувати CGI::escapeпід кришкою.


URI::escapeдобре для уникнення URL-адреси, який не вдалося виконати належним чином. Наприклад, деякі веб-сайти виводять неправильні / незмінені URL-адреси в тезі прив’язки. Якщо ваша програма використовує ці URL-адреси, щоб отримати більше ресурсів, OpenURI поскаржиться, що URL-адреси недійсні. Тобі потрібноURI::escape зробити це дійсним URL-адресою. Таким чином, він використовується для виходу з цілого рядка URI, щоб зробити його належним. У моєму слові URI :: unescape робить URL-адресу, читабельну людиною, а URI :: escape робить її дійсною для браузерів.

Це термін мого мирянина і сміливо виправляю їх.


1

Різниця полягає в тому, що URI.escape не працює ...

CGI.escape"/en/test?asd=qwe"
=> "%2Fen%2Ftest%3Fasd%3Dqwe"

URI.escape"/en/test?asd=qwe"
=> "/en/test?asd=qwe"

2
Ви вибрали неправильний тестовий випадок. / 'S,?' S і = 's - це частина дійсного URI і, таким чином, не уникнути. Інші символи, які потрібно уникати, особливо в рядку запиту, повинні бути.
Джерард ONeill

@GerardONeill Я обрав тестовий випадок саме для того, щоб показати, наскільки URI.escape не працює та ненадійний. Ви припускаєте, що URI.escape уникає лише рядок запиту? як він міг визначити, коли закінчується значення параметра, якщо я хочу кодувати там & in? може, тому це застаріло?
Раду

1
Тобто саме те , що я говорю. Епізод URI повинен проаналізувати URL-адресу, відокремити те, що, на його думку, є окремими параметрами, вийти з них та зібрати їх назад. Навіть це може бути безладно. Але це не робить - він просто уникає втечі деяких персонажів, уникаючи решти, що робить його неповним. Його можна використовувати для простих випадків, особливо якщо ви знаєте, що ваші параметри не будуть .. заплутаними.
Джерард ONeill

0

CGI.escape призначений для уникнення значення URL у рядку запиту. Усі символи, які не потрапляють у ALPHA, DIGIT, '_', '-', '.' та "" набір символів уникнути.

Але це призведе до неправильної URL-адреси, оскільки URL-адреса повинна мати "/", ":", "?", "[',' & ',' = 'І'; '. Можливо, більше того, що я не можу придумати з голови.

URI.escape залишає ці символи URL-адреси в спокої і намагається знайти рядки клавіш і значення для запиту, щоб вийти. Однак від цього насправді не можна залежати, оскільки значення можуть містити всілякі символи, що перешкоджають легкому виходу. В основному, це занадто пізно. Але якщо URL-адреса може залежати від простої (у значеннях немає '&' s та '=' і т. Д.), Ця функція може використовуватися для уникнення, можливо, нечитаних або незаконних символів.

Загалом - завжди використовуйте CGI.escape на окремих клавішах та значеннях, перш ніж з'єднувати їх із "&" та додавати їх після "?".


0

CGI.escape не працював з API OpenProject. Він кодував [],: а не +. Я зламав це разом, що, здається, працює досі для API OpenProject. Але я впевнений, що в ньому відсутні деякі .gsub. Це, мабуть, майже так само погано, як URI.escape, але це не дасть вам застарілих помилок.

class XXX
      def self.encode(path)
        path, query = path.split("?", 2)
        return path if query.nil?
        query = CGI.escape(query).gsub("%3A", ":").gsub("%3D","=").gsub("%5B","[").gsub("%5D","]").gsub("%2C",",").gsub("+","%20")
        return [path,query].join("?")
      end
end

XXX.encode("http://test.com/some/path?query=[box: \"cart\"]")
URI.encode("http://test.com/some/path?query=[box: \"cart\"]")

Обидва вихідні дані:

=> " http://test.com/some/path?query= evidencebox:%20%22cart%22] "
=> " http://test.com/some/path?query= evidencebox:%20 % 22карт.% 22] "

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.