Как безопасно присоединяться к сегментам относительного URL?
Я пытаюсь найти надежный метод объединения сегментов частичного URL-адреса вместе. Есть ли быстрый способ сделать это?
Я попробовал следующее:
puts URI::join('resource/', '/edit', '12?option=test')
Я ожидаю:
resource/edit/12?option=test
Но я получаю ошибку:
`merge': both URI are relative (URI::BadURIError)
Я использовал File.join()
в прошлом для этого, но что-то не похоже на использование библиотеки файлов для URL-адресов.
Ответы
Ответ 1
URI api не всегда велик.
URI:: join будет работать только в том случае, если первый запускается как абсолютный uri с протоколом, а более поздние - относительно правильным образом... за исключением того, что я пытаюсь это сделать и даже не могу получить это Работа.
Это по крайней мере не ошибка, но почему она пропускает средний компонент?
URI::join('http://somewhere.com/resource', './edit', '12?option=test')
Я думаю, что URI просто отстой. Он не имеет значительных api для экземпляров, таких как экземпляр #join или метод для оценки относительно базового uri, который вы ожидаете. Это просто любопытное дерьмо.
Думаю, вам придется писать это самостоятельно. Или просто используйте File.join и другие методы пути к файлу, после тестирования всех крайних случаев, о которых вы можете думать, чтобы убедиться, что он делает то, что вы хотите/ожидаете.
edit 9 декабря 2016 г. Я понял, что addressable gem делает это очень красиво.
base = Addressable::URI.parse("http://example.com")
base + "foo.html"
# => #<Addressable::URI:0x3ff9964aabe4 URI:http://example.com/foo.html>
base = Addressable::URI.parse("http://example.com/path/to/file.html")
base + "relative_file.xml"
# => #<Addressable::URI:0x3ff99648bc80 URI:http://example.com/path/to/relative_file.xml>
base = Addressable::URI.parse("https://example.com/path")
base + "//newhost/somewhere.jpg"
# => #<Addressable::URI:0x3ff9960c9ebc URI:https://newhost/somewhere.jpg>
base = Addressable::URI.parse("http://example.com/path/subpath/file.html")
base + "../up-one-level.html"
=> #<Addressable::URI:0x3fe13ec5e928 URI:http://example.com/path/up-one-level.html>
Ответ 2
Проблема заключается в том, что resource/
относится к текущему каталогу, но /edit
относится к каталогу верхнего уровня из-за ведущей косой черты. Невозможно присоединиться к двум каталогам, не зная наверняка, что edit
содержит resource
.
Если вы ищете чисто строковые операции, просто удалите ведущие или конечные косые черты из всех частей, затем присоедините их к /
как клей.
Ответ 3
Способ сделать это с помощью URI.join:
URI.join('http://example.com', '/foo/', 'bar')
Обратите внимание на конечную косую черту. Здесь вы можете найти полную документацию:
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/uri/rdoc/URI.html#method-c-join
Ответ 4
Использование File.join
не является устойчивым, так как он будет использовать ОСА файловой системы разделитель, который в окнах \
вместо /
, теряя портативность.
Как вы заметили, URI::join
не будет объединять пути с повторяющимися косыми чертами, поэтому он не подходит для части.
Оказывается, для этого не нужно много кода на Ruby:
module GluePath
def self.join(*paths, separator: '/')
paths = paths.compact.reject(&:empty?)
last = paths.length - 1
paths.each_with_index.map { |path, index|
_expand(path, index, last, separator)
}.join
end
def self._expand(path, current, last, separator)
if path.start_with?(separator) && current != 0
path = path[1..-1]
end
unless path.end_with?(separator) || current == last
path = [path, separator]
end
path
end
end
Алгоритм заботится о последовательных слешах, сохраняет начальный и конечный слэши и игнорирует nil
и пустые строки.
puts GluePath::join('resource/', '/edit', '12?option=test')
выходы
resource/edit/12?option=test
Ответ 5
Имеет uri как URI::Generic
или его подкласс
uri.path += '/123'
Наслаждайтесь!
25/06/2016 UPDATE для скептически настроенных людей
require 'uri'
uri = URI('http://ioffe.net/boris')
uri.path += '/123'
p uri
Выходы
<URI::HTTP:0x2341a58 URL:http://ioffe.net/boris/123>
Запустите меня
Ответ 6
Используйте этот код:
File.join('resource/', '/edit', '12?option=test').
gsub(File::SEPARATOR, '/').
sub(/^\//, '')
# => resource/edit/12?option=test
пример с пустыми строками:
File.join('', '/edit', '12?option=test').
gsub(File::SEPARATOR, '/').
sub(/^\//, '')
# => edit/12?option=test
Или используйте это, если возможно, чтобы использовать такие сегменты, как resource/
, edit/
, 12?option=test
и где http:
является только заполнителем для получения действительного URI. Это работает для меня.
URI.
join('http:', 'resource/', 'edit/', '12?option=test').
path.
sub(/^\//, '')
# => "resource/edit/12"
Ответ 7
Я улучшил @Maximo Mussini script, чтобы он работал изящно:
SmartURI.join('http://example.com/subpath', 'hello', query: { token: secret })
=> "http://example.com/subpath/hello?token=secret"
https://gist.github.com/zernel/0f10c71f5a9e044653c1a65c6c5ad697
require 'uri'
module SmartURI
SEPARATOR = '/'
def self.join(*paths, query: nil)
paths = paths.compact.reject(&:empty?)
last = paths.length - 1
url = paths.each_with_index.map { |path, index|
_expand(path, index, last)
}.join
if query.nil?
return url
elsif query.is_a? Hash
return url + "?#{URI.encode_www_form(query.to_a)}"
else
raise "Unexpected input type for query: #{query}, it should be a hash."
end
end
def self._expand(path, current, last)
if path.starts_with?(SEPARATOR) && current != 0
path = path[1..-1]
end
unless path.ends_with?(SEPARATOR) || current == last
path = [path, SEPARATOR]
end
path
end
end
Ответ 8
Вы можете использовать это:
URI.join('http://exemple.com', '/a/', 'b/', 'c/', 'd')
=> #<URI::HTTP http://exemple.com/a/b/c/d>
URI.join('http://exemple.com', '/a/', 'b/', 'c/', 'd').to_s
=> "http://exemple.com/a/b/c/d"
Смотрите: http://ruby-doc.org/stdlib-2.4.1/libdoc/uri/rdoc/URI.html#method-c-join-label-Synopsis
Ответ 9
Мое понимание URI::join
заключается в том, что он мыслит так же, как веб-браузер.
Чтобы оценить его, наведите указатель на свой интеллектуальный веб-браузер на первый параметр и продолжайте нажимать ссылки, пока не перейдете к последнему параметру.
Например, URI::join('http://example.com/resource/', '/edit', '12?option=test')
, вы можете просмотреть это так:
Если первая ссылка была /edit/
(с косой чертой) или /edit/foo
, то следующая ссылка будет относиться к /edit/
, а не к /
.
Возможно, эта страница объясняет это лучше, чем я: Почему URI.join так нелогичен?
Ответ 10
Вы можете использовать File.join('resource/', '/edit', '12?option=test')