Чи можу я використовувати за допомогою безпечного об'єднання 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'

Якщо ні, то як би ви запропонували це зробити без написання коду, повного ifs?



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://
Max Alexander

Ага, так - хороший момент. У таких сценаріях ви хочете зробити щось на кшталт '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. З Документів Документа :

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об’єкт, що зменшує потребу в "коді, повному ifs".


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 аргументи, де як 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


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.
Андрій Михайлов - lolmaus

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 не ігнорує сторонні риски для маршрутизації.
Персеїди

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
Тімоті Цорн


4

Якщо ви використовуєте lodash , ви можете використовувати цей простий oneliner:

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

натхненний @Peter Dotchev в відповідь


3

Це те, що я використовую:

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

Що робити, якщо я намагаюся створити кореневу URL-адресу на зразок цього /assets/foo:? Це призведе до URL-адреси щодо поточного шляху assets/foo.
Андрій Михайлов - lolmaus

1
передбачити нахил? Я маю на увазі, це проста перевірка; ви можете додати його самостійно.
Чесо

3
Ось як це починається ... наступне, що ви знаєте, що ви витратили накопичувальні 8+ годин на пошук кращих справ, які не спрацьовують, та виправлення їх протягом вашого проекту.
камінь

3

Якщо ви використовуєте Angular, ви можете використовувати Location :

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

Працює лише на два аргументи, тому вам доведеться ланцюжок дзвінків або написання функції помічника, щоб це зробити за потреби.


2

Об'єкт 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'

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

0

Це можна досягти за допомогою комбінації шляху та 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 + '/',

З яким я щойно стикався в кодовій базі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.