JavaScript中new URL()跨协议一致获取hostname与pathname的方法
如何忽略协议差异,统一获取URL的hostname和pathname?
在JavaScript中使用new URL()处理不同协议的URL时,会遇到标准协议(如http/https/ftp)和自定义协议(如atp)返回结果不一致的问题:
// 自定义协议atp的解析结果 const url1 = new URL("atp://abcd.com/person/items/1234"); console.log(url1); // 输出: // URL{ // hash: "" // host: "" // hostname: "" // href: "atp://abcd.com/person/items/1234" // origin: "null" // password: "" // pathname: "//abcd.com/person/items/1234" // port: "" // protocol: "atp:" // search: "" // } // 标准协议http的解析结果 const url2 = new URL("http://abcd.com/person/items/1234"); console.log(url2); // 输出: // URL{ // hash: "" // host: "abcd.com" // hostname: "abcd.com" // href: "http://abcd.com/person/items/1234" // origin: "http://abcd.com" // password: "" // pathname: "/person/items/1234" // port: "" // protocol: "http:" // search: "" // }
可以看到,自定义协议的hostname为空,原本的域名被包含在pathname里,而标准协议能正确拆分域名和路径。
解决方案
可以通过两种方式实现统一解析:
方法1:手动修正自定义协议的解析结果
针对hostname为空且pathname以//开头的情况,手动拆分域名和路径:
function getStandardUrlParts(urlStr) { const url = new URL(urlStr); // 处理自定义协议的异常解析 if (!url.hostname && url.pathname.startsWith('//')) { const [hostSegment, ...pathSegments] = url.pathname.slice(2).split('/'); return { hostname: hostSegment, pathname: '/' + pathSegments.join('/'), protocol: url.protocol, fullUrl: url.href }; } // 标准协议直接返回正常结果 return { hostname: url.hostname, pathname: url.pathname, protocol: url.protocol, fullUrl: url.href }; } // 测试用例 console.log(getStandardUrlParts("atp://abcd.com/person/items/1234")); // { hostname: 'abcd.com', pathname: '/person/items/1234', protocol: 'atp:', fullUrl: 'atp://abcd.com/person/items/1234' } console.log(getStandardUrlParts("http://abcd.com/person/items/1234")); // { hostname: 'abcd.com', pathname: '/person/items/1234', protocol: 'http:', fullUrl: 'http://abcd.com/person/items/1234' }
方法2:临时替换协议为标准协议解析
把自定义协议临时替换成http,利用标准协议的解析逻辑获取正确的hostname和pathname,最后还原原协议:
function parseUrlUniformly(urlStr) { const protocolRegex = /^([a-z0-9-]+):\/\//i; const match = urlStr.match(protocolRegex); let tempUrl = urlStr; let originalProtocol = ''; // 识别并替换自定义协议 if (match) { originalProtocol = match[1] + ':'; const standardProtocols = ['http:', 'https:', 'ftp:', 'ftps:']; if (!standardProtocols.includes(originalProtocol.toLowerCase())) { tempUrl = urlStr.replace(protocolRegex, 'http://'); } } const url = new URL(tempUrl); return { hostname: url.hostname, pathname: url.pathname, protocol: originalProtocol || url.protocol, fullUrl: urlStr }; } // 测试用例 console.log(parseUrlUniformly("atp://abcd.com/person/items/1234")); // { hostname: 'abcd.com', pathname: '/person/items/1234', protocol: 'atp:', fullUrl: 'atp://abcd.com/person/items/1234' } console.log(parseUrlUniformly("https://test.org/api/data")); // { hostname: 'test.org', pathname: '/api/data', protocol: 'https:', fullUrl: 'https://test.org/api/data' }
原因说明
浏览器的URL对象仅对标准网络协议遵循URL分层结构解析,会自动拆分//后的内容为hostname和pathname;而自定义协议不在浏览器的标准协议列表中,URL对象会将//及后续内容全部当作pathname处理,导致hostname为空。
内容的提问来源于stack exchange,提问作者SThota
相关产品推荐
相关产品推荐

