Ruby 如何安全地连接相对url段?

Ruby 如何安全地连接相对url段?,ruby,uri,Ruby,Uri,我试图找到一种将部分url路径段连接在一起的健壮方法。有没有快速的方法可以做到这一点 我尝试了以下方法: puts URI::join('resource/', '/edit', '12?option=test') 我期望: resource/edit/12?option=test 但我得到了一个错误: `merge': both URI are relative (URI::BadURIError) 我过去曾使用过File.join(),但使用文件库作为URL似乎有点不正确。问题在于re

我试图找到一种将部分url路径段连接在一起的健壮方法。有没有快速的方法可以做到这一点

我尝试了以下方法:

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

resource/edit/12?option=test
但我得到了一个错误:

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

我过去曾使用过
File.join()
,但使用文件库作为URL似乎有点不正确。

问题在于
resource/
是相对于当前目录的,但是
/edit
由于前导斜杠而指的是顶级目录。如果不知道
edit
包含
resource
,就不可能加入这两个目录


如果您想要纯字符串操作,只需删除所有部分的前导或尾随斜杠,然后使用
/
作为粘合剂将它们连接起来

URI的api并不一定很棒

只有当第一个URI以协议的绝对URI开始,而后面的URI以正确的方式相对时,join才会起作用。。。除非我试着这么做,但我甚至不能让它发挥作用

这至少不会出错,但为什么会跳过中间的组件呢

 URI::join('http://somewhere.com/resource', './edit', '12?option=test') 
我想也许乌里有点差劲。它在实例上缺少重要的api,例如实例连接或相对于基本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>
base=Addressable::URI.parse(“http://example.com")
base+“foo.html”
# => #
base=可寻址::URI.parse(“http://example.com/path/to/file.html")
base+“relative_file.xml”
# => #
base=可寻址::URI.parse(“https://example.com/path")
base+“//newhost/somewhere.jpg”
# => #
base=可寻址::URI.parse(“http://example.com/path/subpath/file.html")
基本+“./up-one-level.html”
=> #

使用URI.join的方法是:

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

注意后面的斜线。您可以在此处找到完整的文档:


您可以使用
File.join('resource/','/edit','12?option=test')
使用以下代码:

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"

正如您所注意到的,
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

将uri作为
uri::Generic
或其子类

uri.path += '/123' 
享受吧

2016年6月25日更新怀疑论者

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

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


我改进了@Maximo Mussini的脚本,使其能够优雅地工作:

SmartURI.join('http://example.com/subpath', 'hello', query: { token: secret })
=> "http://example.com/subpath/hello?token=secret"

您可以使用以下选项:

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"
URI.join('http://exemple.com“,”/a/“,”b/“,”c/“,”d“)
=> #
URI.join('http://exemple.com","a/","b/","c/","d"
=> "http://exemple.com/a/b/c/d"

请参阅:

我对
URI::join的理解是,它的思维方式类似于web浏览器

要对其进行评估,请将mental web浏览器指向第一个参数,并持续单击链接,直到浏览到最后一个参数

例如,
URI::join('http://example.com/resource/“,”/edit“,”12?option=test“
,您可以这样浏览:

  • ,单击指向
    /edit
    (站点根目录下的文件)的链接
  • ,单击指向
    12?option=test
    (与
    edit
    位于同一目录中的文件)的链接
如果第一个链接是
/edit/
(带尾随斜杠)或
/edit/foo
,那么下一个链接将与
/edit/
相关,而不是
/


本页可能比我解释得更好:

一个未优化的解决方案。注意,它不考虑查询参数。它只处理路径

class URL
  def self.join(*str)
    str.map { |path|
      new_path = path
      # Check the first character
      if path[0] == "/"
        new_path = new_path[1..-1]
      end

      # Check the last character
      if path[-1] != "/"
        new_path += "/"
      end

      new_path
    }.join
  end
end

“使用URL文件库似乎有点不对劲”,这是正确的
File.join对操作系统敏感,并将根据操作系统更改用作分隔符的字符。这会给您带来糟糕的结果。除非您不是在域根目录下工作,否则在这种情况下,第一部分的前导
/
会产生不同。这很容易强制使用空字符串作为第一个参数。如果您需要自己管理前导和尾随斜杠,首先使用这个方法有什么意义呢?在这种情况下,您会得到一个URI::HTTP对象。虽然你有一个有效的观点,我通常使用
File.join('http://example.com“,”/foo/“,“bar”)
当我需要一个字符串时,它会处理斜杠。PS:我只在基于linux的系统和服务器上工作,因此我不会面临上述文件分隔符问题。这是一个幸运的巧合,但它不是表达您意图的正确方法。这不会在Windows上使用吗?如果您需要
字符串
版本的uri,请不要忘记执行
操作,否则,您将获得
URI::HTTP
object
Addresable
如果有多个中间段,那么也将跳过中间段。e、 g.
(可寻址::URI.parse(“http://example.com“+”abc“+”xyz“)。to_s
生成
http://example.com/xyz
。在我最初回答这个问题()多年之后,我仍然认为
File.join
是连接URL段的最安全的方式数据这将整个路径组件替换为“/123”,它不会扩展现有路径httpx://example.com/mypath/");
class URL
  def self.join(*str)
    str.map { |path|
      new_path = path
      # Check the first character
      if path[0] == "/"
        new_path = new_path[1..-1]
      end

      # Check the last character
      if path[-1] != "/"
        new_path += "/"
      end

      new_path
    }.join
  end
end