Posso usare require ("path"). Join per concatenare in modo sicuro gli URL?


128

È sicuro da usare require("path").joinper concatenare gli URL, ad esempio:

require("path").join("http://example.com", "ok"); 
//returns 'http://example.com/ok'

require("path").join("http://example.com/", "ok"); 
//returns 'http://example.com/ok'

In caso contrario, che modo suggeriresti di farlo senza scrivere codice pieno di se?



5
Nel caso in cui qualcuno vuole usare path.join, ma i problemi Evita su Windows: path.posix.join('/one/two/three', 'four') // '/one/two/three/four, path.posix.join('/one/two/three/', 'four') // '/one/two/three/four,path.posix.join('/one/two/three/', '/four') // '/one/two/three/four
Timothy Zorn

5
@TimothyZorn Il problema è che se fai qualcosa del genere path.posix.join('http://localhost:9887/one/two/three/', '/four'), il join elimina uno dei doppi tagli inhttp://
Max Alexander

Ahh, sì, buon punto. In questi scenari, che ci si vuole fare qualcosa di simile 'http://localhost:9887/one/two/three/'.replace(/^\/+|\/+$/, '') + '/' + '/four'.replace(/^\/+|\/+$/, '')e si può fare String.prototype.trimSlashes = function() { return this.replace(/^\/+|\/+$/, ''); }se non si desidera digitare l'espressione regolare più e più volte. stackoverflow.com/a/22387870/2537258
Timothy Zorn

oppure['http://localhost:9887/one/two/three/', '/four'].map((part) => part. replace(/^\/+|\/+$/, '')).join('/')
Timothy Zorn

Risposte:


142

No. path.join()restituirà valori errati se utilizzato con gli URL.

Sembra che tu voglia url.resolve. Dai documenti di Node :

url.resolve('/one/two/three', 'four')         // '/one/two/four'
url.resolve('http://example.com/', '/one')    // 'http://example.com/one'
url.resolve('http://example.com/one', '/two') // 'http://example.com/two'

Modifica: come sottolinea correttamente Andreas in un commento, url.resolveaiuterebbe solo se il problema fosse semplice come l'esempio. url.parsesi applica anche a questa domanda perché restituisce campi formattati in modo coerente e prevedibile tramite l' URLoggetto che riduce la necessità di "codice pieno di se".


1
Sebbene non sia esattamente quello che stavo cercando, questo risolve anche il mio problema. Grazie dell'aiuto!
Renato Gama

6
@AndreasHultgren il primo commento è corretto. Se l'esempio fosse, url.resolve('/one/two/three/', 'four')l'output sarebbe 'one/two/three/four'.
tavnab

3
Nel caso in cui qualcuno vuole usare path.join, ma i problemi Evita su Windows: path.posix.join('/one/two/three', 'four') // '/one/two/three/four, path.posix.join('/one/two/three/', 'four') // '/one/two/three/four,path.posix.join('/one/two/three/', '/four') // '/one/two/three/four
Timothy Zorn

2
I commenti non sono corretti, url.resolve('/one/two/three', 'four') // '/one/two/four'la risposta è corretta
Jonathan.

2
Inoltre, tieni presente url.resolve()che richiede solo 2 argomenti, dove as path.join()accetta qualsiasi numero. Quindi, a seconda di cosa stai facendo, potresti dover annidare le chiamate, ad es.url.resolve(url.resolve(SERVER_URL, pagePath), queryString)
Molomby

47

No, non dovresti usare path.join()per unire elementi URL.

C'è un pacchetto per farlo adesso. Quindi, piuttosto che reinventare la ruota, scrivere tutti i tuoi test, trovare bug, risolverli, scrivere più test, trovare un caso limite in cui non funziona, ecc., Potresti usare questo pacchetto.

url-join

https://github.com/jfromaniello/url-join

Installare

npm install url-join

uso

var urljoin = require('url-join');

var fullUrl = urljoin('http://www.google.com', 'a', '/b/cd', '?foo=123');

console.log(fullUrl);

stampe:

" http://www.google.com/a/b/cd?foo=123 "


1
Questo. È fantastico. Grazie.
dudewad


6

Quando ho provato PATH per concatenare parti URL, ho riscontrato problemi. PATH.joinstrisce "//" fino a "/" e in questo modo invalida un URL assoluto (es. http: // ... -> http: / ...). Per me una soluzione rapida è stata:

baseurl.replace(/\/$/,"") + '/' + path.replace(/^\//,"") )

o con la soluzione pubblicata dal colonnello Panic:

[pathA.replace(/^\/|\/$/g,""),pathB.replace(/^\/|\/$/g,"")].join("/")

E se provassi a creare un URL relativo alla radice come questo /assets/foo:? Risulterà in un URL relativo al percorso corrente assets/foo.
Andrey Mikhaylov - lolmaus

5

No! Su Windows path.joinsi unirà con i backslash. Gli URL HTTP sono sempre barre in avanti.

Che ne dite di

> ["posts", "2013"].join("/")
'posts/2013'

Buona idea, ma cosa succede se il primo argomento ha già una barra alla fine? es .: ["posts/", "2013"].join("/")?
Renato Gama

1
@RenatoGama, posts//2013è ancora un URL valido.
Goodwine

2
^ che non funzionerà su tutti i domini, anche se è un URI valido.
BingeBoy

2
In particolare, Node's Express non ignora le barre estranee per il routing.
Perseidi

1
Nel caso in cui qualcuno vuole usare path.join, ma i problemi Evita su Windows: path.posix.join('/one/two/three', 'four') // '/one/two/three/four, path.posix.join('/one/two/three/', 'four') // '/one/two/three/four,path.posix.join('/one/two/three/', '/four') // '/one/two/three/four
Timothy Zorn

5

Lo facciamo in questo modo:

var _ = require('lodash');

function urlJoin(a, b) {
  return _.trimEnd(a, '/') + '/' + _.trimStart(b, '/');
}

3

Se stai usando lodash , puoi usare questo semplice oneliner:

// returns part1/part2/part3
['part1/', '/part2', '/part3/'].map((s) => _.trim(s, '/')).join('/')

ispirato dalla risposta di @Peter Dotchev


3

Se usi Angular, puoi usare Location :

import { Location } from '@angular/common';
// ...
Location.joinWithSlash('beginning', 'end');

Funziona solo su 2 argomenti, quindi devi concatenare chiamate o scrivere una funzione di supporto per farlo se necessario.


2

Questo è quello che uso:

function joinUrlElements() {
  var re1 = new RegExp('^\\/|\\/$','g'),
      elts = Array.prototype.slice.call(arguments);
  return elts.map(function(element){return element.replace(re1,""); }).join('/');
}

esempio:

url = joinUrlElements(config.mgmtServer, '/v1/o/', config.org, '/apps');

E se provassi a creare un URL relativo alla radice come questo /assets/foo:? Risulterà in un URL relativo al percorso corrente assets/foo.
Andrey Mikhaylov - lolmaus

1
anteporre una barra? Voglio dire, è un semplice controllo; puoi aggiungerlo tu stesso.
Cheeso

4
Ecco come inizia ... la prossima cosa che sai di aver trascorso un totale di 8+ ore a trovare casi limite che non funzionano e risolverli nel corso del tuo progetto.
pietra

2

L' oggetto WHATWG URL costruttore ha una (input, base)versione e l' inputpuò essere relativo utilizzo /, ./, ../. Combina questo con path.posix.joine puoi fare qualsiasi cosa:

const {posix} = require ("path");
const withSlash = new URL("https://example.com:8443/something/");
new URL(posix.join("a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/something/a/b/c'
new URL(posix.join("./a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/something/a/b/c'
new URL(posix.join("/a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/a/b/c'
new URL(posix.join("../a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/a/b/c'
const noSlash = new URL("https://example.com:8443/something");
new URL(posix.join("./a", "b", "c"), noSlash).toString(); // 'https://example.com:8443/a/b/c'

0

Soluzione personalizzata per dattiloscritto:

export function pathJoin(parts: string[], sep: string) {
  return parts
    .map(part => {
      const part2 = part.endsWith(sep) ? part.substring(0, part.length - 1) : part;
      return part2.startsWith(sep) ? part2.substr(1) : part2;
    })
    .join(sep);
}

expect(pathJoin(['a', 'b', 'c', 'd'], '/')).toEqual('a/b/c/d');
expect(pathJoin(['a/', '/b/', 'c/', 'd'], '/')).toEqual('a/b/c/d');
expect(pathJoin(['http://abc.de', 'users/login'], '/')).toEqual('http://abc.de/users/login');

0

La mia soluzione

path.join(SERVER_URL, imageAbsolutePath).replace(':/','://');

Modifica: se vuoi supportare gli ambienti Windows

path.join(SERVER_URL, imageAbsolutePath).replace(/\\/g,'/').replace(':/','://');

La seconda soluzione sostituirà tutte le barre rovesciate, quindi anche le parti dell'URL come querystring e hash potrebbero essere modificate, ma l'argomento si unisce solo al percorso dell'URL, quindi non lo considero un problema.


0

Ci sono altre risposte di lavoro, ma sono andato con quanto segue. Una piccola combinazione path.join / URL.

const path = require('path');
//
const baseUrl = 'http://ejemplo.mx';
// making odd shaped path pieces to see how they're handled.
const pieces = ['way//', '//over/', 'there/'];
//
console.log(new URL(path.join(...pieces), baseUrl).href);
// http://ejemplo.mx/way/over/there/

// path.join expects strings. Just an example how to ensure your pieces are Strings.
const allString = ['down', 'yonder', 20000].map(String);
console.log(new URL(path.join(...allString), baseUrl).href);
// http://ejemplo.mx/down/yonder/20000

0

Ciò può essere ottenuto mediante una combinazione del percorso e dell'URL di Node :

  1. Richiedi i pacchetti:
const nodeUrl = require('url')
const nodePath = require('path')
  1. Inizia creando un oggetto URL con cui lavorare:
> const myUrl = new nodeUrl.URL('https://example.com')
  1. Usa pathname=e path.joinper costruire ogni possibile combinazione:
> myUrl.pathname = nodePath.join('/search', 'for', '/something/')
'/search/for/something/'

(puoi vedere quanto path.joinè liberale con gli argomenti)

  1. A questo punto il tuo URL riflette il risultato finale desiderato:
> myUrl.toString()
'https://example.com/search/for/something/'

Perché questo approccio?

Questa tecnica utilizza librerie incorporate. Meno dipendenze da terze parti, meglio è, quando si tratta di CVE, manutenzione, ecc.

PS: non manipolare mai gli URL come stringhe!

Quando esamino il codice sono irremovibile nel non manipolare mai manualmente gli URL come stringhe . Per prima cosa, guarda quanto sono complicate le specifiche .

In secondo luogo, l'assenza / presenza di una barra in coda / con prefisso ( /) non dovrebbe causare la rottura di tutto! Non dovresti mai fare:

const url = `${baseUrl}/${somePath}`

e soprattutto no:

uri: host + '/' + SAT_SERVICE + '/' + CONSTELLATION + '/',

Di cui ho appena incontrato in una base di codice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.