В чем разница между 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 для пробелов, и что это против 'spec' ...
— Луи Сэйерс

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 . В ruby ​​2.0.0 есть модуль URI.escape. Почему это устарело?
— user938363

1
@ user938363 если вы нажмете на источник шоу там, вы увидите, что он все еще помечен как устаревший.
— drewish

229

Какая разница между топором и мечом, и какой мне следует использовать? Ну, это зависит от того, что вам нужно сделать.

URI.escapeдолжен был закодировать строку (URL) в так называемую « процентную кодировку ».

CGI::escapeисходит из спецификации CGI , которая описывает, как данные должны быть закодированы / декодированы между веб-сервером и приложением.

Теперь предположим, что вам нужно экранировать URI в вашем приложении. Это более конкретный вариант использования. Для этого сообщество Ruby использовалось 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.encode - это простой gsub. Но я думаю, что он должен разделить URI на компоненты, затем экранировать каждый компонент и, наконец, присоединиться к ним.

Таким образом, текущий URI.encode считается вредным и устаревшим. Это будет удалено или радикально изменит поведение.

Какая замена в это время?

Как я уже говорил выше, текущий URI.encode неверен на уровне спецификации. Поэтому мы не будем предоставлять точную замену. Замена зависит от варианта использования.

https://bugs.ruby-lang.org/issues/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"

Обратите внимание, что в отличие от всех предыдущих опций Addressable не экранируется #, и это ожидаемое поведение. Вы хотите сохранить #хэш в пути 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если вам нужна только форма 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 на основе ruby ​​завершаются ошибкой с недопустимыми ошибками 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 и = являются частью действительного URI и, следовательно, не экранированы. Другие символы, которые необходимо экранировать, особенно в строке запроса, должны быть.
— Джерард ONeill

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

1
Это именно то, что я говорю. Экран URI должен проанализировать URL-адрес, отделить то, что он считает отдельными параметрами, экранировать их и соединить вместе. Даже это может быть грязно. Но он этого не делает - он просто избегает экранирования некоторых символов при экранировании остальных, что делает его неполным. Это может быть использовано для простых случаев, особенно если вы знаете, что ваши параметры не будут .. запутанными.
— Джерард

0

CGI.escape предназначен для экранирования значения URL в строке запроса. Все символы, которые не попадают в ALPHA, DIGIT, '_', '-', '.' и '' набор символов экранирован.

Но это сделало бы URL неверным, так как URL должен иметь '/', ':', '?', '[', '&', '=' И ';'. Возможно больше, что я не могу думать о макушке.

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

В общем, всегда используйте 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=[box:%20%22cart%22] "
=> " http://test.com/some/path?query=[box:%20 % 22cart% 22] "

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.