ฉันสามารถใช้ require (“ เส้นทาง”) เข้าร่วมเพื่อเชื่อมต่อ 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


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
ทิโมธี Zorn

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
Timothy Zorn

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

คำตอบ:


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'

แก้ไข: ดัง ที่ Andreas ชี้ให้เห็นอย่างถูกต้องในความคิดเห็นurl.resolveจะช่วยได้ก็ต่อเมื่อปัญหานั้นง่ายเหมือนตัวอย่าง url.parseยังใช้กับคำถามนี้เนื่องจากจะส่งคืนฟิลด์ที่จัดรูปแบบอย่างสม่ำเสมอและคาดเดาได้ผ่านURLออบเจ็กต์ซึ่งช่วยลดความต้องการ "โค้ดที่เต็มไปด้วย ifs"


1
แม้ว่าจะไม่ใช่สิ่งที่ฉันกำลังมองหา แต่ก็ช่วยแก้ปัญหาของฉันได้เช่นกัน ขอบคุณสำหรับการช่วยเหลือ!
Renato Gama

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
ทิโมธี Zorn

2
ความคิดเห็นไม่ถูกต้องurl.resolve('/one/two/three', 'four') // '/one/two/four'ในคำตอบถูก
โจนาธาน

2
นอกจากนี้โปรดทราบว่าurl.resolve()จะใช้อาร์กิวเมนต์เพียง 2 อาร์กิวเมนต์โดยpath.join()ใช้จำนวนเท่าใดก็ได้ ดังนั้นขึ้นอยู่กับว่าคุณกำลังทำอะไรอยู่คุณอาจต้องเรียกซ้อนเช่น ..url.resolve(url.resolve(SERVER_URL, pagePath), queryString)
Molomby

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


1
วิธีแก้ปัญหาน่ารักมากคัดลอก + วาง :)
Gilad Peleg

6

เมื่อฉันลอง PATH เพื่อเชื่อมต่อส่วน url ฉันพบปัญหา PATH.joinลายเส้น "//" ลงไปที่ "/" และวิธีนี้จะทำให้ url สัมบูรณ์ไม่ถูกต้อง (เช่น http: // ... -> http: / ... ) สำหรับฉันการแก้ไขอย่างรวดเร็วคือ:

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

หรือด้วยวิธีแก้ปัญหาที่โพสต์โดย Colonel Panic:

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

จะเกิดอะไรขึ้นถ้าฉันพยายามสร้าง URL ที่สัมพันธ์กับรากเช่นนี้: /assets/foo? มันจะส่งผลในปัจจุบันเส้นทางญาติ assets/fooURL
Andrey Mikhaylov - lolmaus

5

No! บน Windowspath.joinจะรวมกับแบ็กสแลช URL ของ HTTP จะมีเครื่องหมายทับเสมอ

เกี่ยวกับ

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

ความคิดที่ดี แต่ถ้าอาร์กิวเมนต์แรกมีเครื่องหมายทับอยู่แล้วล่ะ? เช่น: ["posts/", "2013"].join("/")?
Renato Gama

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
ทิโมธี Zorn


3

หากคุณใช้lodashคุณสามารถใช้ oneliner ง่ายๆนี้:

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

ได้รับแรงบันดาลใจจากคำตอบของ @Peter Dotchev


3

หากคุณใช้ Angular คุณสามารถใช้ตำแหน่ง :

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

ใช้งานได้กับ 2 อาร์กิวเมนต์เท่านั้นดังนั้นคุณต้องต่อสายโซ่หรือเขียนฟังก์ชันตัวช่วยเพื่อทำเช่นนั้นหากจำเป็น


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'

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

จะเกิดอะไรขึ้นถ้าฉันพยายามสร้าง URL ที่สัมพันธ์กับรากเช่นนี้: /assets/foo? มันจะส่งผลในปัจจุบันเส้นทางญาติ assets/fooURL
Andrey Mikhaylov - lolmaus

1
นำหน้าเครื่องหมายทับ? ฉันหมายความว่ามันเป็นการตรวจสอบง่ายๆ คุณสามารถเพิ่มได้เอง
Cheeso

4
นี่คือวิธีเริ่มต้น ... สิ่งต่อไปที่คุณรู้ว่าคุณใช้เวลาสะสม 8+ ชั่วโมงในการค้นหา edge case ที่ใช้งานไม่ได้และแก้ไขตลอดช่วงโครงการของคุณ
หิน

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 เช่น querystring และ hash อาจมีการเปลี่ยนแปลงด้วยเช่นกัน แต่หัวข้อจะเชื่อมโยงกับเส้นทาง 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.