¿Puedo usar require ("ruta"). Join para concatenar urls de forma segura?

128

¿Es seguro usarlo require("path").joinpara concatenar URL, por ejemplo:

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

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

Si no es así, ¿qué manera sugeriría para hacer esto sin escribir un código lleno de if?

Renato Gama
fuente
3
Ver también github.com/joyent/node/issues/2216
Colonel Panic
5
En caso de que alguien quiere usar path.join, pero los problemas de Evita en 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
Timoteo Zorn
5
@TimothyZorn El problema es que si haces algo como esto path.posix.join('http://localhost:9887/one/two/three/', '/four'), la combinación elimina una de las barras dobles enhttp://
Max Alexander
Ahh, sí, buen punto. En esos escenarios, querrá hacer algo como 'http://localhost:9887/one/two/three/'.replace(/^\/+|\/+$/, '') + '/' + '/four'.replace(/^\/+|\/+$/, '')y podría hacerlo String.prototype.trimSlashes = function() { return this.replace(/^\/+|\/+$/, ''); }si no desea escribir la expresión regular una y otra vez. stackoverflow.com/a/22387870/2537258
Timothy Zorn
o['http://localhost:9887/one/two/three/', '/four'].map((part) => part. replace(/^\/+|\/+$/, '')).join('/')
Timothy Zorn

Respuestas:

142

No. path.join()devolverá valores incorrectos cuando se use con URL.

Suena como si quisieras url.resolve. De los documentos de 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'

Editar: Como Andreas señala correctamente en un comentario, url.resolvesolo ayudaría si el problema es tan simple como el ejemplo. url.parsetambién se aplica a esta pregunta porque devuelve campos formateados de manera consistente y predecible a través del URLobjeto que reduce la necesidad de "código lleno de ifs".

Matthew Bakaitis
fuente
1
Aunque no es exactamente lo que estaba buscando, esto también resuelve mi problema. ¡Gracias por ayudar!
Renato Gama
6
@AndreasHultgren el primer comentario es correcto. Si el ejemplo fuera url.resolve('/one/two/three/', 'four'), la salida sería 'one/two/three/four'.
tavnab
3
En caso de que alguien quiere usar path.join, pero los problemas de Evita en 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
Timoteo Zorn
2
Los comentarios son incorrectos, url.resolve('/one/two/three', 'four') // '/one/two/four'la respuesta es correcta
Jonathan.
2
También tenga en cuenta que url.resolve()solo toma 2 argumentos, donde as path.join()toma cualquier número. Entonces, dependiendo de lo que estés haciendo, es posible que url.resolve(url.resolve(SERVER_URL, pagePath), queryString)
debas
47

No, no debe utilizar path.join()para unir elementos de URL.

Hay un paquete para hacer eso ahora. Entonces, en lugar de reinventar la rueda, escribir todas sus propias pruebas, encontrar errores, corregirlos, escribir más pruebas, encontrar un caso extremo donde no funcione, etc., podría usar este paquete.

url-join

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

Instalar en pc

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);

Huellas dactilares:

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

Roca
fuente
1
Esta. Esto es fantástico. Gracias.
dudewad
6

Cuando probé PATH para concatenar partes de URL, me encontré con problemas. PATH.joinrayas '//' hasta '/' y de esta manera invalida una URL absoluta (por ejemplo, http: // ... -> http: / ...). Para mí, una solución rápida fue:

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

o con la solución publicada por Colonel Panic:

[pathA.replace(/^\/|\/$/g,""),pathB.replace(/^\/|\/$/g,"")].join("/")
Pedro
fuente
¿Qué sucede si estoy intentando crear una URL relativa a la raíz como esta /assets/foo:? Dará como resultado una URL relativa a la ruta actual assets/foo.
Andrey Mikhaylov - lolmaus
5

¡No! En Windows path.joinse unirá con barras invertidas. Las URL HTTP son siempre barras inclinadas.

Qué tal si

> ["posts", "2013"].join("/")
'posts/2013'
Coronel Panic
fuente
Buena idea, pero ¿y si el primer argumento ya tiene una barra al final? por ejemplo ["posts/", "2013"].join("/"):?
Renato Gama
1
@RenatoGama, posts//2013sigue siendo una URL válida.
Goodwine
2
^ que no funcionará en todos los dominios, aunque sea un URI válido.
BingeBoy
2
Específicamente, Node's Express no ignora las barras oblicuas extrañas para el enrutamiento.
Perseidas
1
En caso de que alguien quiere usar path.join, pero los problemas de Evita en 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
Timoteo Zorn
5

Lo hacemos así:

var _ = require('lodash');

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

Si está usando lodash , puede usar este sencillo delineador:

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

inspirado en la respuesta de @Peter Dotchev

MK
fuente
3

Si usa Angular, puede usar Location :

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

Sin embargo, solo funciona con 2 argumentos, por lo que debe encadenar llamadas o escribir una función auxiliar para hacerlo si es necesario.

Qortex
fuente
2

Esto es lo que uso:

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

ejemplo:

url = joinUrlElements(config.mgmtServer, '/v1/o/', config.org, '/apps');
Cheeso
fuente
¿Qué sucede si estoy intentando crear una URL relativa a la raíz como esta /assets/foo:? Dará como resultado una URL relativa a la ruta actual assets/foo.
Andrey Mikhaylov - lolmaus
1
anteponer una barra? Quiero decir, es un simple cheque; puede agregarlo usted mismo.
Cheeso
4
Así es como comienza ... Lo siguiente que sabe es que ha pasado más de 8 horas acumuladas para encontrar casos extremos que no funcionan y solucionarlos en el transcurso de su proyecto.
piedra
2

El objeto WHATWG URL constructor tiene una (input, base)versión y el inputpuede ser relativa usando /, ./, ../. Combine esto con path.posix.joiny puede hacer cualquier 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'
Coderer
fuente
0

Solución personalizada de 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
fuente
0

Mi solución

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

Editar: si desea admitir entornos de Windows

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

La segunda solución reemplazará todas las barras invertidas, por lo que las partes de la URL como la cadena de consulta y el hash también pueden modificarse, pero el tema se une solo a la ruta de la URL, por lo que no lo considero un problema.

Killy
fuente
0

Hay otras respuestas que funcionan, pero fui con lo siguiente. Un pequeño 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
fuente
0

Esto se puede lograr mediante una combinación de la ruta y la URL del nodo :

  1. Requerir los paquetes:
const nodeUrl = require('url')
const nodePath = require('path')
  1. Empiece por crear un objeto URL con el que trabajar:
> const myUrl = new nodeUrl.URL('https://example.com')
  1. Utilice pathname=y path.joinpara construir cualquier combinación posible:
> myUrl.pathname = nodePath.join('/search', 'for', '/something/')
'/search/for/something/'

(puedes ver lo liberal que path.joines con los argumentos)

  1. En este punto, su URL refleja el resultado final deseado:
> myUrl.toString()
'https://example.com/search/for/something/'

¿Por qué este enfoque?

Esta técnica utiliza bibliotecas integradas. Cuantas menos dependencias de terceros, mejor, cuando se trata de CVE, mantenimiento, etc.

PD: ¡Nunca manipules las URL como cadenas!

Cuando reviso el código, soy inflexible sobre nunca manipular las URL como cadenas manualmente . Por un lado, mire lo complicada que es la especificación .

En segundo lugar, la ausencia / presencia de una barra inclinada ( /) al final o prefijada no debería hacer que todo se rompa. Nunca deberías hacer:

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

y especialmente no:

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

De los cuales acabo de encontrar en una base de código.

Sean Patrick Murphy
fuente