Puis-je utiliser require («path»). Join pour concaténer des URL en toute sécurité?

128

Est-ce sûr à utiliser require("path").joinpour concaténer des URL, par exemple:

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

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

Sinon, que suggéreriez-vous pour faire cela sans écrire de code plein de ifs?

Renato Gama
la source
3
Voir aussi github.com/joyent/node/issues/2216
Colonel Panic
5
Dans le cas où quelqu'un veut utiliser path.join, mais il faut éviter les problèmes sous 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 Le problème est que si vous faites quelque chose comme ça path.posix.join('http://localhost:9887/one/two/three/', '/four'), la jointure élimine l' une des doubles barres obliques danshttp://
Max Alexander
Ahh, ouais - bon point. Dans ces scénarios, vous voudriez faire quelque chose comme 'http://localhost:9887/one/two/three/'.replace(/^\/+|\/+$/, '') + '/' + '/four'.replace(/^\/+|\/+$/, '')et vous pourriez le faire String.prototype.trimSlashes = function() { return this.replace(/^\/+|\/+$/, ''); }si vous ne voulez pas taper l'expression régulière encore et encore. stackoverflow.com/a/22387870/2537258
Timothy Zorn
ou['http://localhost:9887/one/two/three/', '/four'].map((part) => part. replace(/^\/+|\/+$/, '')).join('/')
Timothy Zorn

Réponses:

142

No. path.join()renverra des valeurs incorrectes lorsqu'il est utilisé avec des URL.

On dirait que tu veux url.resolve. À partir de la documentation 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'

Edit: Comme Andreas le souligne correctement dans un commentaire, url.resolvecela n'aiderait que si le problème est aussi simple que l'exemple. url.parses'applique également à cette question car elle renvoie des champs formatés de manière cohérente et prévisible via l' URLobjet, ce qui réduit le besoin de «code plein de if».

Matthew Bakaitis
la source
1
Bien que ce ne soit pas exactement ce que je recherchais, cela résout également mon problème. Merci pour ton aide!
Renato Gama
6
@AndreasHultgren le premier commentaire est correct. Si l'exemple était url.resolve('/one/two/three/', 'four')alors la sortie serait 'one/two/three/four'.
tavnab
3
Dans le cas où quelqu'un veut utiliser path.join, mais il faut éviter les problèmes sous 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
Les commentaires sont incorrects, url.resolve('/one/two/three', 'four') // '/one/two/four'la réponse est correcte
Jonathan.
2
Sachez également qu'il url.resolve()ne prend que 2 arguments, où as path.join()prend n'importe quel nombre. Donc, selon ce que vous faites, vous devrez peut-être imbriquer des appels, par exemple ..url.resolve(url.resolve(SERVER_URL, pagePath), queryString)
Molomby
47

Non, vous ne devez pas utiliser path.join()pour joindre des éléments d'URL.

Il y a un paquet pour faire ça maintenant. Donc, plutôt que de réinventer la roue, d'écrire tous vos propres tests, de trouver des bogues, de les corriger, d'écrire plus de tests, de trouver un cas limite où cela ne fonctionne pas, etc., vous pouvez utiliser ce paquet.

jointure d'URL

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

Installer

npm install url-join

Usage

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

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

console.log(fullUrl);

Impressions:

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

calcul
la source
1
Ce. C'est fantastique. Je vous remercie.
dudewad
6

Quand j'ai essayé PATH pour concaténer des parties d'url, j'ai rencontré des problèmes. PATH.joinstripes '//' jusqu'à '/' et de cette façon invalide une URL absolue (par exemple. http: // ... -> http: / ...). Pour moi, une solution rapide était:

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

ou avec la solution publiée par le colonel Panic:

[pathA.replace(/^\/|\/$/g,""),pathB.replace(/^\/|\/$/g,"")].join("/")
Peter
la source
Et si j'essaie de créer une URL relative à la racine comme celle-ci /assets/foo:? Il en résultera une URL relative au chemin actuel assets/foo.
Andrey Mikhaylov - lolmaus
5

Non! Sur Windows path.joinse joindra avec des barres obliques inverses. Les URL HTTP sont toujours des barres obliques.

Que diriez-vous

> ["posts", "2013"].join("/")
'posts/2013'
Colonel Panic
la source
Bonne idée, mais que faire si le premier argument a déjà une barre oblique à la fin? par exemple .: ["posts/", "2013"].join("/")?
Renato Gama
1
@RenatoGama, posts//2013est toujours une URL valide.
Goodwine
2
^ qui ne fonctionnera pas sur tous les domaines, même s'il s'agit d'un URI valide.
BingeBoy
2
Plus précisément, Node's Express n'ignore pas les barres obliques superflues pour le routage.
Perséides
1
Dans le cas où quelqu'un veut utiliser path.join, mais il faut éviter les problèmes sous 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

Nous le faisons comme ceci:

var _ = require('lodash');

function urlJoin(a, b) {
  return _.trimEnd(a, '/') + '/' + _.trimStart(b, '/');
}
Peter Dotchev
la source
3

Si vous utilisez lodash , vous pouvez utiliser ce simple oneliner:

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

inspiré par la réponse de @Peter Dotchev

MK
la source
3

Si vous utilisez Angular, vous pouvez utiliser Location :

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

Cela ne fonctionne que sur 2 arguments, vous devez donc enchaîner les appels ou écrire une fonction d'assistance pour le faire si nécessaire.

Qortex
la source
2

Voici ce que j'utilise:

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

exemple:

url = joinUrlElements(config.mgmtServer, '/v1/o/', config.org, '/apps');
Cheeso
la source
Et si j'essaie de créer une URL relative à la racine comme celle-ci /assets/foo:? Il en résultera une URL relative au chemin actuel assets/foo.
Andrey Mikhaylov - lolmaus
1
ajouter une barre oblique? Je veux dire, c'est un simple chèque; vous pouvez l'ajouter vous-même.
Cheeso
4
C'est comme ça que ça commence ... la prochaine chose que vous savez que vous avez passé plus de 8 heures cumulées à trouver des cas extrêmes qui ne fonctionnent pas et à les réparer au cours de votre projet.
pierre du
2

L' objet URL WHATWG constructeur a une (input, base)version et inputpeut être relatif à l' aide /, ./, ../. Combinez cela avec path.posix.joinet vous pouvez tout faire:

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'
Codeur
la source
0

Solution personnalisée Typescript:

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');
Patrick Wozniak
la source
0

Ma solution

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

Modifier: si vous souhaitez prendre en charge les environnements Windows

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

La deuxième solution remplacera toutes les barres obliques inverses, de sorte que les parties d'URL comme la chaîne de requête et le hachage peuvent également être modifiées, mais le sujet ne rejoint que le chemin de l'url, donc je ne considère pas cela comme un problème.

Killy
la source
0

Il y a d'autres réponses de travail, mais je suis allé avec ce qui suit. Un petit combo 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
Neil Guy Lindberg
la source
0

Cela peut être accompli par une combinaison du chemin et de l' URL de Node :

  1. Exiger les packages:
const nodeUrl = require('url')
const nodePath = require('path')
  1. Commencez par créer un objet URL avec lequel travailler:
> const myUrl = new nodeUrl.URL('https://example.com')
  1. Utilisez pathname=et path.joinpour construire toute combinaison possible:
> myUrl.pathname = nodePath.join('/search', 'for', '/something/')
'/search/for/something/'

(vous pouvez voir à quel point les path.joinarguments sont libéraux )

  1. À ce stade, votre URL reflète le résultat ultime souhaité:
> myUrl.toString()
'https://example.com/search/for/something/'

Pourquoi cette approche?

Cette technique utilise des bibliothèques intégrées. Moins il y a de dépendances tierces, mieux c'est, en ce qui concerne les CVE, la maintenance, etc.

PS: ne manipulez jamais les URL comme des chaînes!

Lorsque j'examine le code, je suis catégorique sur le fait de ne jamais manipuler manuellement les URL en tant que chaînes . D'une part, regardez à quel point la spécification est compliquée .

Deuxièmement, l'absence / présence d'une barre oblique de fin / préfixée ( /) ne doit pas tout casser! Vous ne devriez jamais faire:

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

et surtout pas:

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

Dont je viens de rencontrer dans une base de code.

Sean Patrick Murphy
la source