如何测试 URL 字符串是绝对的还是相对的?

Posted

技术标签:

【中文标题】如何测试 URL 字符串是绝对的还是相对的?【英文标题】:How to test if a URL string is absolute or relative? 【发布时间】:2012-05-28 01:30:13 【问题描述】:

如果 URL 是 javascript 或 jQuery 中的相对或绝对路径,我如何测试它?我想根据传入的 URL 是本地路径还是外部路径进行相应处理。

if (urlString starts with http:// or https://)
 //do this

【问题讨论】:

【参考方案1】:

快速

如果您只需要测试http://https://,那么最有效的方法是:

if (urlString.indexOf('http://') === 0 || urlString.indexOf('https://') === 0)

通用

不过,我建议采用更通用、不区分大小写、与协议无关的方法

var r = new RegExp('^(?:[a-z]+:)?//', 'i');
r.test('http://example.com'); // true - regular http absolute URL
r.test('HTTP://EXAMPLE.COM'); // true - HTTP upper-case absolute URL
r.test('https://www.exmaple.com'); // true - secure http absolute URL
r.test('ftp://example.com/file.txt'); // true - file transfer absolute URL
r.test('//cdn.example.com/lib.js'); // true - protocol-relative absolute URL
r.test('/myfolder/test.txt'); // false - relative URL
r.test('test'); // false - also relative URL

解释正则表达式

^(?:[a-z]+:)?//

^ - 字符串的开头(?: - 未捕获组的开头 [a-z]+ - 'a' 到 'z' 的任何字符 1 次或更多次@ 987654329@ - 字符串(冒号))? - 未捕获组的结尾。组出现 0 或 1 次// - 字符串(两个正斜杠字符)'i' - 不区分大小写标志

【讨论】:

为什么是 a-z ?域名不能有0-9和连字符吗? 对,但我们这里不检查域名,是吗?这仍然有效:/^(?:[a-z]+:)?\/\//i.test('https://www.ex-maple-123.com'); 方案可以包含数字吗?我们都知道 http、https、ftp 和 mailto。有人为内部工具定义自定义方案吗?我认为 OneNote 和 Outlook 在 Windows 上可以。 这不会捕获“mailto:” URL。并不是说我知道 mailto URL 是绝对的还是相对的 ;-) new RegExp('^(//|[a-z]+:)', 'i') 应该适用于匹配mailto:about:tel: 等,包括现有的测试用例。这里的想法是仍然提供协议相关的绝对 URL,同时扩展检测绝对 URL 的现有功能,而不需要检查双正斜杠 (//)。因此,r.test('mailto:hi@example.com') === truer.test('https:example.com') === true 等等。【参考方案2】:
var pat = /^https?:\/\//i;
if (pat.test(urlString))

    //do stuff

对于协议相关的 url,使用这个正则表达式:

/^https?:\/\/|^\/\//i

【讨论】:

这回答了所提出的问题,但您可能还需要考虑以// 开头的protocol relative urls。 如果 url 包含“file://”怎么办?繁荣!悲剧。 @Philipp 的回答更可靠。 接受的答案是无效的,至少在 2019 年是这样。Chrome 很乐意接受 http:example.com。【参考方案3】:

原答案

一个非常快速和非常灵活的检查是:

if (url.indexOf('://') > 0 || url.indexOf('//') === 0 ) 
    // URL is absolute; either "http://example.com" or "//example.com"
 else 
    // URL is relative

这将识别绝对 URL,如果:

URL 在第一个字符之后 处包含“://”,或 网址以“//”开头(相对于协议)
没有正则表达式。 没有 jQuery 或其他依赖项。 没有使条件区分大小写的硬编码协议名称。 没有字符串操作(例如 toLowerCase 或类似的)。 仅检查“相对或绝对”但不进行任何其他健全性检查,可用于 Web URL 或任何内部协议。

更新 1(完整功能示例)

这是一个快速的函数,它为给定的 URL 返回真/假:

function isUrlAbsolute(url)  
    return (url.indexOf('://') > 0 || url.indexOf('//') === 0);

在 ES6 中也是如此:

const isUrlAbsolute = (url) => (url.indexOf('://') > 0 || url.indexOf('//') === 0)

更新 2(URL 参数中的 URL)

要另外以/redirect?target=http://example.org 格式寻址 URL,我建议使用以下代码:

function isUrlAbsolute(url) 
    if (url.indexOf('//') === 0) return true; // URL is protocol-relative (= absolute)
    if (url.indexOf('://') === -1) return false; // URL has no protocol (= relative)
    if (url.indexOf('.') === -1) return false; // URL does not contain a dot, i.e. no TLD (= relative, possibly REST)
    if (url.indexOf('/') === -1) return false; // URL does not contain a single slash (= relative)
    if (url.indexOf(':') > url.indexOf('/')) return false; // The first colon comes after the first slash (= relative)
    if (url.indexOf('://') < url.indexOf('.')) return true; // Protocol is defined before first dot (= absolute)
    return false; // Anything else must be relative

简写形式和 ES 6 相同

// Traditional JS, shortened
function isUrlAbsolute(url) 
    return url.indexOf('//') === 0 ? true : url.indexOf('://') === -1 ? false : url.indexOf('.') === -1 ? false : url.indexOf('/') === -1 ? false : url.indexOf(':') > url.indexOf('/') ? false : url.indexOf('://') < url.indexOf('.') ? true : false;


// ES 6
const isUrlAbsolute = (url) => (url.indexOf('//') === 0 ? true : url.indexOf('://') === -1 ? false : url.indexOf('.') === -1 ? false : url.indexOf('/') === -1 ? false : url.indexOf(':') > url.indexOf('/') ? false : url.indexOf('://') < url.indexOf('.') ? true : false)

这里有一些测试用例:

// Test
console.log( isUrlAbsolute('http://***.com') ) // -> true
console.log( isUrlAbsolute('//***.com') ) // -> true
console.log( isUrlAbsolute('***.com') ) // -> false
console.log( isUrlAbsolute('Ftp://example.net') ) // -> true
console.log( isUrlAbsolute('/redirect?target=http://example.org') ) // -> false

更新 3(澄清相对 URL)

我见过一些关于无效输出的cmets:

localhost 的解决方案返回 false http:example.com回答失败

但是,这些 URL 确实是相对 URL。很容易测试:

    在您的 localhost webroot 上创建一些文件夹,例如 a/b/c/ 创建一个 index.html 文件并将以下链接放入其中:&lt;a href="localhost"&gt;test&lt;/a&gt; 在浏览器中打开索引页面:http://localhost/a/b/c/index.html 并单击链接。您将在http://localhost/a/b/c/localhost(而不是http://localhost)结束 将链接 http:example.com 放入 index.html 文件时也会发生同样的情况。你以http://localhost/a/b/c/example.com 结束而不是http://example.com

【讨论】:

不。我只是在跟踪我的项目中的一个错误,发现它也是一个这样的功能。该网页的网址类似于/redirect?target=http://example.org @BeniBela,你可以使用function isUrlAbsolute(url) var firstSlash = url.indexOf('/'); var colonDoubleSlash = url.indexOf('://'); return ((firstSlash &gt; 0 &amp;&amp; colonDoubleSlash &gt; 0 &amp;&amp; colonDoubleSlash &lt; firstSlash) || url.indexOf('//') === 0); 来解决这个问题 @BeniBela 你是对的,在某些情况下可能会发生这种情况。我更新了上面的代码来处理这个问题。但是,我强烈建议对所有查询参数进行 url 编码,即使用 /redirect?target=http%3A%2F%2Fexample.com 这回答了这个问题,但它并没有真正测试输入是否是绝对的。例如,“/aaa/bbb”作为“相对”返回,而实际上它是绝对的。 isUrlAbsolute('redirect') 给出false,这是正确的,但isUrlAbsolute('redirect?target=http://example.org') 给出true,这是不正确的。我认为检查:// 是否出现在?# 之后会很有用……有什么情况会与某些东西发生冲突吗?【参考方案4】:

使用正则表达式:

if (/^(?:[a-z]+:)?\/\//i.test(url))

【讨论】:

这似乎是最普遍的答案。仅缺少相对于协议的 URL(例如 //cdn.example.com/libary.js) 虽然问题只提到了 http 和 https,但一般的解决方案可能还必须考虑到“mailto:” url,它没有正斜杠。 @mikebridge 你是说mailto: 有时可以是绝对的或相对的? @Geo:不;他是说mailto: 是绝对的,即使它没有/ 字符。 请在此处加入聊天chat.***.com/rooms/44712/absolute-or-relative-url【参考方案5】:

根据您的需要,我认为确定这一点的更可靠方法是使用built-in URL interface 构造几个 URL 对象并比较来源。

new URL(document.baseURI).origin === new URL(urlToTest, document.baseURI).origin;

这允许浏览器为您解析和计算所有这些,而不必担心边缘情况的副作用。

【讨论】:

这是对其他更多鸭式解决方案的一个很好的新增功能。但是我想知道您为什么不建议 new URL(document.baseURI).origin === new URL(urlToTest,document.baseURI).origin ?这不是更适合网页包含&lt;base&gt; 的情况吗? @humanityANDpeace 是的,好主意!我已经根据您的改进更新了答案。 我对每一个基于正则表达式的答案都投了反对票,并对每一个使用像 URL 这样的内置类的答案投了赞成票。这是正确的答案。谢谢!【参考方案6】:

更符合通用 RFC 的 URI 方法:

(?:^[a-z][a-z0-9+\.-]*:|\/\/)regex explanation

此处列出的其他解决方案对于 mailto:evan@nylas.com 之类的链接将失败

RFC 3986方案 定义为:

scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )

3.1.方案 https://www.rfc-editor.org/rfc/rfc3986#section-3.1

虽然根据第 4.2 节,相对于协议的 url 在技术上是有效的,但 Paul Irish 却反过来认为这是一种反模式。见http://www.paulirish.com/2010/the-protocol-relative-url/

4.2.相对参考 https://www.rfc-editor.org/rfc/rfc3986#section-4.2

如果您想要不使用相对协议网址的正则表达式:

^[a-z][a-z0-9+\.-]*:

要查看其他类型的有效 uri 边缘情况的完整列表,请在此处查看列表:https://en.wikipedia.org/wiki/URI_scheme

【讨论】:

^应该出群吗?正如所写的那样,它将在非起始位置匹配//(因此像#// 这样的相对URL 将匹配)。此外,指定此正则表达式不区分大小写也很重要,因此完整的定义应类似于 /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i 我认为应该将单字符方案视为驱动器号。所以我用+替换* 您的正则表达式匹配//cdn.example.com/lib.js,这是一个相对URI,而不是绝对URI:datatracker.ietf.org/doc/html/rfc3986#section-4.2“以两个斜杠字符开头的相对引用称为网络路径引用;”跨度> 【参考方案7】:

现在很多服务都使用protocol-relative URL(例如//cdn.example.com/libary.js),这种方式比较安全:

var isAbsolute = new RegExp('^([a-z]+://|//)', 'i');

if (isAbsolute.test(urlString)) 
  // go crazy here

【讨论】:

要捕获像'HTTP://WWW.GOOGLE.COM'这样的网址,你应该使用'^([A-Za-z]+://|//)' 只需设置i 标志以忽略大小写。答案已编辑。谢谢。【参考方案8】:

不要使用正则表达式等低级的东西。这些东西已经被很多人解决了。尤其是边缘情况。

看看URI.js,它应该可以完成这项工作:http://medialize.github.io/URI.js/docs.html#is

var uri = new URI("http://example.org/");
uri.is("absolute") === true;

【讨论】:

如果您有很多操作要做,这很有用,但为此使用 JS 库似乎有点过头了。【参考方案9】:

这是一个非常强大的浏览器环境解决方案:

让浏览器处理一切。不需要一些复杂/容易出错的正则表达式。

const isAbsoluteUrl = (url) => 
  const link = document.createElement('a');
  link.href = url;
  return link.origin + link.pathname + link.search + link.hash === url;
;

【讨论】:

【参考方案10】:

您可以使用 try, catch 块来帮助解决此问题。您可以在每一步都使用URL 接口,而不是使用正则表达式。

isExternalUrl (urlString) 
  try 
    const url = new URL(urlString) // THROW ON MISSING SCHEME

    // DOES THIS URL ORIGINATE FROM THIS WEBSITE?
    if (url.origin !== new URL(document.URL, document.baseURI).origin) 
      return true // IS EXTERNAL URL
    
   catch (_e) 
    // THROWS WHEN URL DOES NOT HAVE A SCHEME
    new URL(urlString, document.baseURL) // THROW AN EXCEPTION IF THE URL IS TRULY MALFORMED IN SOME WAY
  

  return false

【讨论】:

8年后,这是最好的答案【参考方案11】:
var external = RegExp('^(https?:)?//');
if(external.test(el))
    // do something

编辑:

使用下一个正则表达式,您甚至可以检查链接是指向同一个域还是指向外部域:

var external = RegExp('^((f|ht)tps?:)?//(?!' + location.host + ')');
if(external.test(el))
    // do something

【讨论】:

您需要转义几乎肯定会出现在主机名中的. 字符。否则 foo.example.com 也会匹配 fooXexample.com【参考方案12】:
var adress = 'http://roflmao.com';
if (adress.substr(0,7) == 'http://' || adress.substr(0,8) == 'https://') 
    //

【讨论】:

是的,这是真的。我不使用正则表达式,因为我很讨厌它。反正现代浏览器不会把Http转换成http吗?【参考方案13】:

上述解决方案均未解决黑客输入/\/example.com/\\/example.comredirect_url 黑客攻击。这是我用来确定我们的重定向 url 是否是相对的:

var isRelative = !redirectUrl.match(/(\:|\/\\*\/)/);  // Don't allow "//" (with optional "\"'s) or ":"

【讨论】:

【参考方案14】:

它不应该以斜杠或哈希开头,如果前面没有问号或哈希,它不应该包含双斜杠?我不会用一个正则表达式来测试,匹配“没有双斜杠”会非常复杂。

function test(s) 
    return s.charAt(0) != "#"
      && s.charAt(0) != "/"
      && ( s.indexOf("//") == -1 
        || s.indexOf("//") > s.indexOf("#")
        || s.indexOf("//") > s.indexOf("?")
    );

会更容易、更清晰、更快。

【讨论】:

【参考方案15】:

当超链接上发生单击事件时将调用以下函数页面将在新的浏览器选项卡中加载

jQuery(document).ready(function() 
    $('a').click(function()

        var a = this;
        var a_href = $(this).attr('href');
        var regex = new RegExp('^(?:[a-z]+:)?//', 'i');     

        if(a.host == location.host || regex.test(a_href) == false)
            a.target = '_self';
        else
            a.target = '_blank';
        
    ); 
);

【讨论】:

【参考方案16】:
var isExternalURL = url.toLowerCase().indexOf('http://') === 0 || url.toLowerCase().indexOf('https://') === 0 ;

【讨论】:

以上是关于如何测试 URL 字符串是绝对的还是相对的?的主要内容,如果未能解决你的问题,请参考以下文章

仅打印绝对 URL

Java - 如果我知道域,如何将相对 URL 字符串更改为绝对 URL?

相对表单操作解析为绝对 URL?

来自 C# 中的基本 URL + 相对 URL 的绝对 URL

请教一个PHP中相对url和绝对url的问题

绝对与相对 URL