2012-01-17 24 views
24

我試圖找到一個連接部分url路徑段的強大方法。有沒有快速的方法來做到這一點?我如何安全地加入相對的網址?

我試過如下:

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

我想到:

resource/edit/12?option=test 

但我得到的錯誤:

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

我已經在過去使用File.join()對於這一點,但東西似乎不正確的使用文件庫的網址。

+1

「的東西看起來不正確有關使用文件庫網址爲」,這是正確的。 'File.join'對操作系統很敏感,並且會根據操作系統改變用作分隔符的字符。那會給你帶來不好的結果。 – 2012-01-17 22:13:46

回答

7

問題是resource/是相對於當前目錄,但/edit由於前導斜槓引用頂層目錄。如果不知道edit包含resource,就不可能加入這兩個目錄。

如果您正在尋找純粹的字符串操作,只需從所有部分中刪除前導或尾部的斜槓,然後將它們與/作爲膠水一起使用。

+1

+1,是的,不需要'/'。 – 2012-01-17 22:11:58

+0

那麼,除非你不在域根上工作,在這種情況下,第一部分的前導'/'會起作用。 – Tim 2012-01-17 22:22:45

+0

使用空字符串作爲第一個參數很容易。 – 2012-01-17 22:23:37

17

的URI API不neccearily很大。

URI ::加入只有當第一個開始了與協議中的絕對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> 
+2

如果你需要uri的字符串版本,不要忘記做'to_s',否則你會得到'URI :: HTTP'對象 – 2015-04-24 11:52:11

+0

你需要添加像'http:// somewhere.com/resource /'的尾部斜槓 – urmurmur 2016-12-09 13:29:25

6

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

+10

如果你需要自己管理前後斜線,首先使用這種方法有什麼意義? – eremzeit 2013-08-21 22:39:39

+0

在這種情況下,您會得到一個URI :: HTTP對象。雖然你有一個有效的觀點,當我需要一個字符串並且需要斜線時,我通常使用File.join('http://example.com','/ foo /','bar')'。 PS:我只是在基於Linux的系統和服務器上工作,所以我不面臨上述的文件分隔符問題。 – 2013-08-22 07:49:55

-1

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

+9

這是一個幸運的巧合,但它不是打電話表達你的意圖的正確方法。 – 2014-03-27 23:32:51

+2

這不會在Windows上使用\? – lpil 2016-04-06 08:46:39

2

使用此代碼:

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

例如空字符串:

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

使用File.join因爲我不健壯t將使用操作系統文件系統分隔符,它在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 
1

有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> 

Run me

+0

這將用「/ 123」替換整個路徑組件,但不會擴展現有路徑。 – 2016-10-25 13:54:08

+0

不,它不是。在發佈之前,我嘗試過。 – bioffe 2016-10-25 13:55:47

+0

下面是我得到的: uri = URI(「httpx://example.com/mypath/」); uri + ='/ 123' =># 2016-10-25 14:07:33

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