Как безопасно присоединить относительные сегменты URL? - PullRequest
28 голосов
/ 17 января 2012

Я пытаюсь найти надежный способ объединения частичных сегментов пути URL. Есть ли быстрый способ сделать это?

Я попробовал следующее:

puts URI::join('resource/', '/edit', '12?option=test')

Я ожидаю:

resource/edit/12?option=test

Но я получаю ошибку:

`merge': both URI are relative (URI::BadURIError)

Я использовал File.join() в прошлом для этого, но что-то не так с использованием файловой библиотеки для URL.

Ответы [ 9 ]

21 голосов
/ 18 января 2012

API URI не обязательно велик.

URI :: join будет работать только в том случае, если первый из них начинается с абсолютного URI с протоколом, а последующие являются относительными в правильных отношениях ... за исключением того, что я пытаюсь это сделать и не могу дажезаставить это работать.

По крайней мере, это не ошибка, но почему пропускается средний компонент?

 URI::join('http://somewhere.com/resource', './edit', '12?option=test') 

Я думаю, что URI просто отстой.Он не имеет значительного API для экземпляров, таких как экземпляр #join или метод для оценки относительно базового URI, который вы ожидаете.Это просто дерьмо.

Я думаю, вам придется написать это самостоятельно.Или просто используйте File.join и другие методы пути к файлу, после тестирования всех крайних случаев, о которых вы можете подумать, чтобы убедиться, что он делает то, что вы хотите / ожидаете.

edit 9 Dec 2016 Я понял, что адресуемый гем делает это очень хорошо.

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>
8 голосов
/ 17 января 2012

Проблема в том, что resource/ относится к текущему каталогу, а /edit относится к каталогу верхнего уровня из-за начального слеша.Невозможно объединить эти два каталога, не зная наверняка, что edit содержит resource.

Если вы ищете чисто строковые операции, просто удалите начальную или конечную косую черту из всех частей, а затем соедините их с / в качестве клея.

7 голосов
/ 30 января 2012

Способ сделать это с помощью URI.join:

URI.join('http://example.com', '/foo/', 'bar')

Обратите внимание на косые черты. Вы можете найти полную документацию здесь:

http://www.ruby -doc.org / STDLIB-1.9.3 / libdoc / URI / RDoc / URI.html # способ-с-присоединиться

4 голосов
/ 04 октября 2016

Иметь Uri как URI::Generic или его подкласс

uri.path += '/123' 

Наслаждайтесь!

06/25/2016 ОБНОВЛЕНИЕ для скептиков

require 'uri'
uri = URI('http://ioffe.net/boris')
uri.path += '/123'
p uri

Выходы

 <URI::HTTP:0x2341a58 URL:http://ioffe.net/boris/123>

Запустите меня

4 голосов
/ 05 января 2016

Использование File.join не является надежным, поскольку он будет использовать разделитель файловой системы ОС, который в Windows равен \ вместо /, теряя переносимость.

Как вы заметили, 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')

выходы * * 1016

resource/edit/12?option=test
2 голосов
/ 04 сентября 2015

Используйте этот код:

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"
0 голосов
/ 08 сентября 2017

Вы можете использовать это:

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

0 голосов
/ 25 августа 2017

Я улучшил скрипт @Maximo Mussini, чтобы он работал изящно:

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
0 голосов
/ 18 октября 2013

Вы можете использовать File.join('resource/', '/edit', '12?option=test')

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...