2012-01-17 11 views
24

Sto cercando di trovare un metodo efficace per unire insieme segmenti di percorso url parziali. C'è un modo rapido per farlo?Come posso accedere in modo sicuro ai segmenti di url relativi?

ho provato la seguente:

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

mi aspetto:

resource/edit/12?option=test 

Ma ottengo l'errore:

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

ho usato File.join() in passato per questo, ma qualcosa non sembra giusto sull'utilizzo della libreria di file per url.

+1

"qualcosa non mi sembra giusto su come utilizzare la libreria di file per gli URL", che è corretto. 'File.join' è sensibile al sistema operativo e cambierà il carattere utilizzato come separatore, a seconda del sistema operativo. Questo ti darebbe cattivi risultati. –

risposta

7

Il problema è che resource/ è relativo alla directory corrente, ma /edit si riferisce alla directory di livello superiore a causa della barra iniziale. È impossibile unire le due directory senza sapere già per certo che edit contiene resource.

Se si cercano operazioni puramente a corda, è sufficiente rimuovere le barre iniziali o finali da tutte le parti, quindi unirle a / come colla.

+1

+1, sì, '/' non sono necessari. –

+0

Bene, a meno che non si stia lavorando alla radice del dominio, nel qual caso il principale '/' nella prima parte farebbe la differenza. – Tim

+0

È facile forzare l'utilizzo di una stringa vuota come primo parametro. –

17

L'API di URI non è eccezionale.

URI :: join funzionerà solo se il primo inizia come un assoluto uri con protocollo, e quelli successivi sono relativi nei giusti modi ... eccetto che provo a farlo e non riesco nemmeno a ottenerlo lavorare.

Questo almeno non errore, ma perché si salta il componente centrale?

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

Penso che forse l'URI sia solo una specie di schifo. Manca un'API significativa su istanze, come un'istanza #join o un metodo per valutare relativamente a un uri di base, che ti aspetteresti. È solo un po 'schifoso.

Penso che dovrai scriverlo da solo. Oppure usa solo i metodi File.join e altri percorsi del file, dopo aver provato tutti i casi limite che ti vengono in mente per assicurarti che faccia ciò che vuoi/aspetti.

modifica 9 dic 2016 ho capito che la gemma addressable lo fa molto bene.

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

Non dimenticare di fare 'to_s' se hai bisogno della versione' String' di uri, altrimenti ottieni l'oggetto 'URI :: HTTP' –

+0

Devi aggiungere una barra finale come' http: // somewhere.com/resource/' – urmurmur

6

Il modo per farlo utilizzando URI.join è:

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

Prestare attenzione alle slash.È possibile trovare la documentazione completa qui:

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

+10

Se hai bisogno di gestire da solo le barre iniziali e finali, che senso ha usare questo metodo? – eremzeit

+0

In questo caso si ottiene un oggetto URI :: HTTP. Sebbene tu abbia un punto valido, e generalmente uso 'File.join ('http://example.com', '/ foo /', 'bar')' quando ho bisogno di una stringa e si prende cura delle barre. PS: Io lavoro esclusivamente su sistemi e server basati su linux, quindi non affronto il problema del separatore di file descritto sopra. –

-1

È possibile utilizzare File.join('resource/', '/edit', '12?option=test')

+9

Questa è una coincidenza fortunata, ma non è il metodo giusto per chiamare per esprimere la tua intenzione. –

+2

Non userà \ su Windows? – lpil

2

di questo codice:

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

esempio con le stringhe vuote:

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

Utilizzando File.join non è robusto da quando io t utilizzerà il separatore del file system del sistema operativo, che in Windows è \ anziché /, con conseguente perdita della portabilità.

Come notato, lo URI::join non combina i percorsi con barre ripetute, quindi non si adatta alla parte.

scopre che non richiede un sacco di codice Ruby per raggiungere questo obiettivo:

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 

L'algoritmo si prende cura di tagli consecutivi, conserva inizio e fine barre, e ignora nil e vuoti stringhe.

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

uscite

resource/edit/12?option=test 
1

uri avere come URI::Generic o sottoclasse di esso

uri.path += '/123' 

Enjoy!

06/25/2016 UPDATE per la gente scettica

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

uscite

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

Run me

+0

Sostituisce l'intero componente del percorso con '/ 123', non estende il percorso esistente. –

+0

No, non lo è. Ho provato prima di postare questo. – bioffe

+0

Ecco cosa ottengo: uri = URI ("httpx: //esempio.com/mypath/"); uri + = '/ 123' => #

0

ho migliorato la sceneggiatura di @Maximo Mussini per rendere funziona con garbo:

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 
Problemi correlati