我正在尝试找到一种将部分url路径段连接在一起的强大方法。有快速的方法吗?
我尝试了以下内容:
puts URI::join('resource/', '/edit', '12?option=test')
我期待:
resource/edit/12?option=test
但我收到错误:
`merge': both URI are relative (URI::BadURIError)
我过去曾使用过File.join()
,但是对于使用网址文件库来说似乎不对。
答案 0 :(得分:21)
URI的api并不是很好。
URI :: join只有在第一个以协议开头的绝对uri时才有效,而后者以正确的方式相对...除了我尝试这样做,甚至不能得到它工作。
这至少没有错误,但为什么它会跳过中间组件?
URI::join('http://somewhere.com/resource', './edit', '12?option=test')
我认为URI可能很糟糕。它在实例上缺少重要的api,例如实例#join或相对于基本uri的方法,你期望它。这有点糟糕。
我想你将不得不自己写。或者只是使用File.join和其他文件路径方法,在测试了所有可以想到的边缘情况后,确保它能够满足您的需求。
编辑 2016年12月9日我发现addressable宝石非常好。
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>
答案 1 :(得分:8)
问题是resource/
是相对于当前目录的,但/edit
由于前导斜杠而引用顶级目录。如果不确定edit
包含resource
,则无法加入这两个目录。
如果您正在寻找纯粹的字符串操作,只需从所有部分中删除前导或尾部斜杠,然后将其与/
作为粘合剂连接起来。
答案 2 :(得分: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
答案 3 :(得分:4)
将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 :(得分:3)
使用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.starts_with?(separator) && current != 0
path = path[1..-1]
end
unless path.ends_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 :(得分:2)
使用此代码:
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"
答案 6 :(得分:1)
未优化的解决方案。请注意,它不考虑查询参数。它只处理路径。
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
答案 7 :(得分:0)
我改进了@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
答案 8 :(得分:0)
您可以使用:
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 :(得分:0)
我对URI::join
的理解是,它像Web浏览器一样。
要对其进行评估,请将您的心理网络浏览器指向第一个参数,并不断单击链接,直到浏览到最后一个参数。
例如URI::join('http://example.com/resource/', '/edit', '12?option=test')
,您将像这样浏览:
/edit
(网站根目录中的文件)的链接12?option=test
(与edit
在同一目录中的文件)的链接如果第一个链接是/edit/
(带有斜杠)或/edit/foo
,则下一个链接将相对于/edit/
而不是/
。
此页面可能比我能更好地解释它:Why is URI.join so counterintuitive?
答案 10 :(得分:-1)
您可以使用File.join('resource/', '/edit', '12?option=test')