it-swarm.com.de

Wie kann ich relative URL-Segmente sicher verbinden?

Ich versuche, eine robuste Methode zum Zusammenfügen von Teil-URL-Pfadsegmenten zu finden. Gibt es eine schnelle Möglichkeit, dies zu tun?

Ich habe folgendes versucht:

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

Ich erwarte:

resource/edit/12?option=test

Aber ich bekomme den Fehler:

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

Ich habe File.join() in der Vergangenheit dafür verwendet, aber es scheint nicht richtig, die Dateibibliothek für URLs zu verwenden.

26
Tim Santeford

URI's API ist nicht gerade großartig. 

URI :: join funktioniert nur, wenn der erste als absoluter uri mit dem Protokoll beginnt und die späteren relativ auf die richtige Art und Weise sind ... außer ich versuche das zu tun und kann das nicht einmal zum Laufen bringen. 

Dies ist zumindest kein Fehler, aber warum überspringt es die mittlere Komponente?

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

Ich denke, dass URI vielleicht irgendwie scheiße ist. Es gibt keine signifikanten API-Instanzen, wie etwa eine #Join-Instanz oder eine Methode, die relativ zu einem Basis-URI zu bewerten ist, die Sie erwarten würden. Es ist nur irgendwie beschissen. 

Ich denke, du musst es selbst schreiben. Oder verwenden Sie einfach File.join und andere Dateipfadmethoden. Nachdem Sie alle Edge-Fälle getestet haben, die Sie sich vorstellen können, stellen Sie sicher, dass sie das tun, was Sie wollen/erwarten. 

edit 9 Dec 2016 Ich habe herausgefunden, dass adressierbar gem sehr gut funktioniert. 

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>
21
jrochkind

Das Problem ist, dass resource/ relativ zum aktuellen Verzeichnis ist, /edit jedoch aufgrund des führenden Schrägstrichs auf das Verzeichnis der obersten Ebene verweist. Es ist unmöglich, die beiden Verzeichnisse zusammenzuschließen, ohne zu wissen, dass editresource enthält. 

Wenn Sie nach reinen Zeichenkettenoperationen suchen, entfernen Sie einfach die führenden oder nachgestellten Schrägstriche von allen Teilen und fügen Sie sie mit / als Klebstoff zusammen. 

8
Tim

Der Weg mit URI.join ist wie folgt:

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

Achten Sie auf die nachgestellten Schrägstriche. Die vollständige Dokumentation finden Sie hier:

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

6
amit_saxena

Habe uri als URI::Generic oder eine Unterklasse davon 

uri.path += '/123' 

Genießen!

25.06.2016 UPDATE für skeptische Leute

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

Ausgänge

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

Lauf mich

4
bioffe

Die Verwendung von File.join ist nicht robust, da hier das OS-Dateisystem-Trennzeichen verwendet wird. In Windows heißt es \ anstelle von /, wodurch die Portabilität verloren geht.

Wie Sie bemerkt haben, kombiniert URI::join keine Pfade mit wiederholten Schrägstrichen, sodass es nicht zum Teil passt.

Es stellt sich heraus, dass es nicht viel Ruby-Code benötigt, um dies zu erreichen:

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

Der Algorithmus kümmert sich um aufeinanderfolgende Schrägstriche, behält die Anfangs- und End-Schrägstriche bei und ignoriert nil und leere Zeichenfolgen.

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

ausgänge

resource/edit/12?option=test
3
Maximo Mussini

Verwenden Sie diesen Code:

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

beispiel mit leeren Zeichenketten:

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

Oder verwenden Sie dies, wenn möglich, um Segmente wie resource/, edit/, 12?option=test zu verwenden, und wobei http: nur ein Platzhalter ist, um einen gültigen URI zu erhalten. Das funktioniert für mich.

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

Ich habe @Maximo Mussinis Skript verbessert, damit es einwandfrei funktioniert:

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
0
Zernel

Sie können dies verwenden:

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"

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

0
gndm