如何安全地加入相对网址段?

Tim*_*ord 30 ruby uri

我试图找到一种将部分url路径段连接在一起的可靠方法.有快速的方法吗?

我尝试了以下方法:

puts URI::join('resource/', '/edit', '12?option=test')
Run Code Online (Sandbox Code Playgroud)

我预计:

resource/edit/12?option=test
Run Code Online (Sandbox Code Playgroud)

但我得到错误:

`merge': both URI are relative (URI::BadURIError)
Run Code Online (Sandbox Code Playgroud)

File.join()过去曾经使用过这个,但是对于使用url文件库来说似乎并不正确.

jro*_*ind 21

URI的api并不是很好.

URI :: join只有在第一个以协议开头的绝对uri时才有效,而后者以正确的方式相对...除了我尝试这样做,甚至无法让它工作.

这至少没有错误,但为什么它会跳过中间组件?

 URI::join('http://somewhere.com/resource', './edit', '12?option=test') 
Run Code Online (Sandbox Code Playgroud)

我想也许URI有点糟透了.它在实例上缺少重要的api,例如实例#join或相对于基本uri的方法,你期望它.这有点糟糕.

我想你将不得不自己写.或者只是使用File.join和其他文件路径方法,在测试了所有可以想到的边缘情况后,确保它能够满足您的需求.

编辑 2016年12月9日我发现可寻址的宝石非常好.

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>
Run Code Online (Sandbox Code Playgroud)

  • 如果您需要`String`版本的uri,请不要忘记执行`to_s`,否则您将获得`URI :: HTTP`对象 (2认同)
  • 如果有多个,`addresable`也会跳过中间部分。例如`(Addressable :: URI.parse(“ http://example.com”)+“ abc” +“ xyz”)。to_s`生成`http:// example.com / xyz`。在我对这个问题的原始答案(/sf/answers/634742391/)多年之后,我仍然认为`File.join`是连接URL段的最安全方法。:D (2认同)

bio*_*ffe 9

将 uri 作为URI::Generic或它的子类

uri.path += '/123' 
Run Code Online (Sandbox Code Playgroud)

享受!

2016 年 6 月 25 日对持怀疑态度的人的更新

require 'uri'
uri = URI('http://ioffe.net/boris')
uri.path += '/123'
p uri
Run Code Online (Sandbox Code Playgroud)

输出

 <URI::HTTP:0x2341a58 URL:http://ioffe.net/boris/123>
Run Code Online (Sandbox Code Playgroud)

跑我

  • @AneilMallavarapu你应该考虑你做错了什么。在这种情况下,您应该使用 url.path 而不是 url。在否决合法答案之前,请仔细查看我提供的示例。 (2认同)

Tim*_*Tim 8

问题是resource/相对于当前目录,但/edit由于前导斜杠而引用顶级目录.如果没有确切知道edit包含这两个目录,就不可能加入这两个目录resource.

如果您正在寻找纯粹的字符串操作,只需从所有部分中删除前导或尾部斜杠,然后将它们/作为粘合剂连接起来.


ami*_*ena 7

使用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

  • 如果您需要自己管理前导和尾随斜杠,那么首先使用此方法的重点是什么? (15认同)

Max*_*ini 6

正如您所注意到的,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
Run Code Online (Sandbox Code Playgroud)

该算法处理连续斜线,保留开始和结束斜线,并忽略nil和空字符串。

puts GluePath::join('resource/', '/edit', '12?option=test')
Run Code Online (Sandbox Code Playgroud)

产出

resource/edit/12?option=test
Run Code Online (Sandbox Code Playgroud)