Могу ли я использовать require («path»). Join для безопасного объединения URL-адресов?

128

Безопасно ли это использовать require("path").joinдля объединения URL-адресов, например:

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

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

Если нет, то какой способ сделать это без написания кода, полного «если»?

Ренато Гама
источник
3
См. Также github.com/joyent/node/issues/2216
Colonel Panic
5
В случае , если кто -то хочет использовать path.join, но проблемы не следует применять в 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
Тимоти Цорн
5
@TimothyZorn Проблема в том, что если вы сделаете что-то вроде этого path.posix.join('http://localhost:9887/one/two/three/', '/four'), объединение избавится от одной из двойных косых http://
Макс Александр
Ах, да - хорошее замечание. В этих сценариях вы захотите сделать что-то подобное, 'http://localhost:9887/one/two/three/'.replace(/^\/+|\/+$/, '') + '/' + '/four'.replace(/^\/+|\/+$/, '')и вы могли бы это сделать, String.prototype.trimSlashes = function() { return this.replace(/^\/+|\/+$/, ''); }если не хотите вводить регулярное выражение снова и снова. stackoverflow.com/a/22387870/2537258
Тимоти Зорн
или['http://localhost:9887/one/two/three/', '/four'].map((part) => part. replace(/^\/+|\/+$/, '')).join('/')
Тимоти Цорн

Ответы:

142

Нет path.join(), вернет неверные значения при использовании с URL-адресами.

Похоже, ты хочешь url.resolve. Из документов 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'

Изменить: как правильно указывает Андреас в комментарии, url.resolveпоможет только в том случае, если проблема так же проста, как пример. url.parseтакже относится к этому вопросу, потому что он возвращает последовательно и предсказуемо отформатированные поля через URLобъект, что снижает потребность в «коде, полном if».

Мэтью Бакайтис
источник
1
Хотя это не совсем то, что я искал, это тоже решает мою проблему. Спасибо за помощь!
Ренато Гама
6
@AndreasHultgren, первый комментарий правильный. Если бы был пример, url.resolve('/one/two/three/', 'four')то вывод был бы 'one/two/three/four'.
tavnab
3
В случае , если кто -то хочет использовать path.join, но проблемы не следует применять в 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
Тимоти Цорн
2
В комментариях неверно, url.resolve('/one/two/three', 'four') // '/one/two/four'в ответ правильный
Джонатан.
2
Также имейте в url.resolve()виду, что принимает только 2 аргумента, где as path.join()принимает любое число. Так что, в зависимости от того, что вы делаете, вам может потребоваться url.resolve(url.resolve(SERVER_URL, pagePath), queryString)
вложить
47

Нет, вы не должны использовать path.join()для присоединения элементов URL.

Для этого есть пакет. Поэтому вместо того, чтобы изобретать велосипед, писать все свои собственные тесты, находить ошибки, исправлять их, писать больше тестов, находить крайний случай, когда это не работает, и т. Д., Вы можете использовать этот пакет.

URL присоединиться

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

устанавливать

npm install url-join

использование

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

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

console.log(fullUrl);

Печать:

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

камень
источник
1
Это. Это фантастика. Спасибо.
dudewad
7

Axios имеет вспомогательную функцию, которая может комбинировать URL-адреса.

function combineURLs(baseURL, relativeURL) {
  return relativeURL
    ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
    : baseURL;
}

Источник: https://github.com/axios/axios/blob/fe7d09bb08fa1c0e414956b7fc760c80459b0a43/lib/helpers/combineURLs.js

Ikbel
источник
1
Очень милое решение, копировать + вставить :)
Гилад Пелег
6

Когда я попробовал PATH для объединения частей URL, я столкнулся с проблемами. PATH.joinполосы '//' вниз до '/', и таким образом аннулируется абсолютный URL (например, http: // ... -> http: / ...). Для меня быстрое исправление было:

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

или с решением, опубликованным полковником Паником:

[pathA.replace(/^\/|\/$/g,""),pathB.replace(/^\/|\/$/g,"")].join("/")
Питер
источник
Что делать, если я пытаюсь создать URL-адрес, относящийся к корню, например /assets/foo:? Это приведет к URL-адресу, относящемуся к текущему пути assets/foo.
Андрей Михайлов - лолмаус
5

Нет! В Windows path.joinбудет соединяться с обратной косой чертой. URL-адреса HTTP всегда представляют собой косую черту.

Как насчет

> ["posts", "2013"].join("/")
'posts/2013'
Полковник Паник
источник
Хорошая идея, но что, если первый аргумент уже имеет косую черту в конце? напр .: ["posts/", "2013"].join("/")?
Ренато Гама
1
@RenatoGama posts//2013по-прежнему является действующим URL.
Goodwine
2
^, который не будет работать во всех доменах, даже если это действительный URI.
BingeBoy
2
В частности, Node's Express не игнорирует лишние косые черты при маршрутизации.
Perseids
1
В случае , если кто -то хочет использовать path.join, но проблемы не следует применять в 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
Тимоти Цорн
5

Делаем это так:

var _ = require('lodash');

function urlJoin(a, b) {
  return _.trimEnd(a, '/') + '/' + _.trimStart(b, '/');
}
Петр Дочев
источник
3

Если вы используете lodash , вы можете использовать этот простой лайнер:

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

вдохновленный @Peter Dotchev в ответ

MK
источник
3

Если вы используете Angular, вы можете использовать Location :

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

Однако работает только с двумя аргументами, поэтому вам нужно связать вызовы или написать вспомогательную функцию, чтобы сделать это, если это необходимо.

Qortex
источник
3

Объект WHATWG URL - конструктор имеет (input, base)версию, и inputможет быть относительным использованием /, ./, ../. Совместите это с, path.posix.joinи вы можете сделать что угодно:

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
источник
2

Вот что я использую:

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

пример:

url = joinUrlElements(config.mgmtServer, '/v1/o/', config.org, '/apps');
Cheeso
источник
Что делать, если я пытаюсь создать URL-адрес, относящийся к корню, например /assets/foo:? Это приведет к URL-адресу, относящемуся к текущему пути assets/foo.
Андрей Михайлов - лолмаус
1
добавить косую черту? Я имею в виду, это простая проверка; вы можете добавить это сами.
Cheeso
4
Вот как это начинается ... Следующее, что вы знаете, вы потратили в общей сложности 8+ часов на поиск крайних случаев, которые не работают, и исправление их в ходе вашего проекта.
камень
1

Этого можно добиться с помощью комбинации пути и URL-адреса узла :

  1. Требуются пакеты:
const nodeUrl = require('url')
const nodePath = require('path')
  1. Начните с создания объекта URL, с которым будет работать:
> const myUrl = new nodeUrl.URL('https://example.com')
  1. Используйте pathname=и path.joinдля построения любой возможной комбинации:
> myUrl.pathname = nodePath.join('/search', 'for', '/something/')
'/search/for/something/'

(вы можете видеть, насколько либеральны path.joinаргументы)

  1. На этом этапе ваш URL-адрес отражает окончательный желаемый результат:
> myUrl.toString()
'https://example.com/search/for/something/'

Почему такой подход?

В этом методе используются встроенные библиотеки. Чем меньше сторонних зависимостей, тем лучше, когда дело касается CVE, обслуживания и т. Д.

PS: Никогда не манипулируйте URL-адресами как строками!

Когда я просматриваю код, я категорически против никогда не манипулировать URL-адресами как строками вручную . Во-первых, посмотрите, насколько сложна спецификация .

Во-вторых, отсутствие / наличие завершающей / префиксной косой черты ( /) не должно приводить к поломке всего! Никогда не делайте:

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

и особенно нет:

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

С которыми я только что столкнулся в кодовой базе.

Шон Патрик Мерфи
источник
0

Индивидуальное решение 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');
Патрик Возняк
источник
0

Мое решение

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

Изменить: если вы хотите поддерживать среды Windows

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

Второе решение заменит все обратные косые черты, поэтому части URL, такие как строка запроса и хэш, также могут быть изменены, но тема присоединяется только к пути URL, поэтому я не считаю это проблемой.

Килли
источник
0

Есть и другие рабочие ответы, но я выбрал следующее. Небольшая комбинация 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
Нил Гай Линдберг
источник