ruby 如何安全地加入相对 url 段?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8900782/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 04:41:40  来源:igfitidea点击:

How do I safely join relative url segments?

rubyuri

提问by Tim Santeford

I'm trying to find a robust method of joining partial url path segments together. Is there a quick way to do this?

我正在尝试找到一种将部分 url 路径段连接在一起的强大方法。有没有快速的方法来做到这一点?

I tried the following:

我尝试了以下方法:

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

I expect:

我预计:

resource/edit/12?option=test

But I get the error:

但我收到错误:

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

I have used File.join()in the past for this but something does not seem right about using the file library for urls.

File.join()过去曾为此使用过,但是将文件库用于 url 似乎有些不对劲。

回答by jrochkind

URI's api is not neccearily great.

URI 的 api 并不是很好。

URI::join will work only if the first one starts out as an absolute uri with protocol, and the later ones are relative in the right ways... except I try to do that and can't even get that to work.

URI::join 仅在第一个开始时作为具有协议的绝对 uri 时才起作用,而后面的则以正确的方式是相对的......除非我尝试这样做并且甚至无法使其工作。

This at least doesn't error, but why is it skipping the middle component?

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

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

I think maybe URI just kind of sucks. It lacks significant api on instances, such as an instance #join or method to evaluate relative to a base uri, that you'd expect. It's just kinda crappy.

我认为 URI 可能有点糟糕。它在实例上缺少重要的 api,例如实例 #join 或相对于基本 uri 进行评估的方法,这是您所期望的。只是有点烂。

I think you're going to have to write it yourself. Or just use File.join and other File path methods, after testing all the edge cases you can think of to make sure it does what you want/expect.

我想你将不得不自己写。或者只是使用 File.join 和其他文件路径方法,在测试了所有你能想到的边缘情况之后,以确保它符合你的要求/期望。

edit9 Dec 2016 I figured out the addressablegem does it very nicely.

2016 年 12 月 9 日编辑我发现可寻址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>

回答by Tim

The problem is that resource/is relative to the current directory, but /editrefers to the top level directory due to the leading slash. It's impossible to join the two directories without already knowing for certain that editcontains resource.

问题是resource/相对于当前目录,但/edit由于前导斜杠指的是顶级目录。在不知道edit包含resource.

If you're looking for purely string operations, simply remove the leading or trailing slashes from all parts, then join them with /as the glue.

如果您正在寻找纯粹的字符串操作,只需删除所有部分的前导或尾随斜线,然后将它们/作为胶水连接起来。

回答by amit_saxena

The way to do it using URI.join is:

使用 URI.join 的方法是:

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

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

Pay attention to the trailing slashes. You can find the complete documentation here:

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

http://www.ruby-doc.org/stdlib-1.9.3/libdoc/uri/rdoc/URI.html#method-c-join

http://www.ruby-doc.org/stdlib-1.9.3/libdoc/uri/rdoc/URI.html#method-c-join

回答by Maximo Mussini

Using File.joinis not robust since it will use the OS filesystem separator, which in Windows is \instead of /, losing portability.

使用File.join不是健壮的,因为它将使用操作系统文件系统分隔符,在 Windows 中它\代替/,失去可移植性。

As you noticed, URI::joinwon't combine paths with repeated slashes, so it doesn't fit the part.

正如您所注意到的,URI::join不会将路径与重复的斜杠组合在一起,因此它不适合该部分。

Turns out it doesn't require a lot of Ruby code to achieve this:

事实证明,它不需要很多 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

The algorithm takes care of consecutive slashes, preserves start and end slashes, and ignores niland empty strings.

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

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

outputs

产出

resource/edit/12?option=test

回答by bioffe

Have uri as URI::Genericor subclass of thereof

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

uri.path += '/123' 

Enjoy!

享受!

06/25/2016 UPDATEfor skeptical folk

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

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

Outputs

输出

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

Run me

跑我

回答by phlegx

Use this code:

使用此代码:

File.join('resource/', '/edit', '12?option=test').
     gsub(File::SEPARATOR, '/').
     sub(/^\//, '')
# => resource/edit/12?option=test

example with empty strings:

空字符串示例:

File.join('', '/edit', '12?option=test').
     gsub(File::SEPARATOR, '/').
     sub(/^\//, '')
# => edit/12?option=test

Or use this if possible to use segments like resource/, edit/, 12?option=testand where http:is only a placeholder to get a valid URI. This works for me.

或者,如果可能用它来使用领域,如resource/edit/12?option=test并在那里http:只是一个占位符来获得一个有效的URI。这对我有用。

URI.
  join('http:', 'resource/', 'edit/', '12?option=test').
  path.
  sub(/^\//, '')
# => "resource/edit/12"

回答by Zernel

I improved @Maximo Mussini's script to make it works gracefully:

我改进了@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

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

回答by gndm

You can use this:

你可以使用这个:

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"

See: http://ruby-doc.org/stdlib-2.4.1/libdoc/uri/rdoc/URI.html#method-c-join-label-Synopsis

请参阅:http: //ruby-doc.org/stdlib-2.4.1/libdoc/uri/rdoc/URI.html#method-c-join-label-Synopsis

回答by mwfearnley

My understanding of URI::joinis that it thinks like a web browser does.

我的理解URI::join是它像网络浏览器一样思考。

To evaluate it, point your mental web browser to the first parameter, and keep clicking links until you browse to the last parameter.

要评估它,请将您的心理 Web 浏览器指向第一个参数,并继续单击链接,直到浏览到最后一个参数。

For example, URI::join('http://example.com/resource/', '/edit', '12?option=test'), you would browse like this:

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

If the first link were /edit/(with a trailing slash), or /edit/foo, then the next link would be relative to /edit/rather than /.

如果第一个链接是/edit/(带有尾部斜杠)或/edit/foo,则下一个链接将相对于/edit/而不是/

This page possibly explains it better than I can: Why is URI.join so counterintuitive?

这个页面可能比我能更好地解释它:为什么 URI.join 如此违反直觉?

回答by HaxElit

You can use File.join('resource/', '/edit', '12?option=test')

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