检查一个JavaScript字符串是否是一个URL

552

在JavaScript中,有没有一种方法可以检查字符串是否为URL?

正则表达式被排除在外,因为URL最可能像stackoverflow这样写,也就是说它可能没有.comwwwhttp


36
如果缺少 http,它默认不是一个URL。 - nfechner
2
@nfechner 如果它没有指定协议并使用冒号字符(最好是紧随其后的两个正斜杠),那么它就不是一个URL? - jcolebrand
7
如你所见在URL RFC中,使字符串成为有效的URL所必需的唯一部分是冒号。有效的URL看起来像:<scheme>:<scheme-specific-part>。 - nfechner
1
请参考以下链接:https://dev59.com/KXM_5IYBdhLWcg3wmkiu#3975573 - nguyên
13
如何测试某个东西是否为 URL 是高度依赖上下文的,没有更进一步的限定就太模糊了。你在意它是否符合 URL RFC 规范、在进行操作系统调用时打开 URL 是否正常、作为锚元素的 href 解析是否正确、调用 window.open(url) 是否有效、指向实际存在的内容、在浏览器地址栏中是否能够工作或以上多种情况的组合对你是否有意义?你将根据你关注的内容得到非常不同的答案。 - Roy Tinker
显示剩余8条评论
39个回答

634
如果你想检查一个字符串是否为有效的HTTP URL,可以使用URL构造函数(如果字符串格式不正确会抛出异常):
function isValidHttpUrl(string) {
  let url;
  
  try {
    url = new URL(string);
  } catch (_) {
    return false;  
  }

  return url.protocol === "http:" || url.protocol === "https:";
}

注意:根据RFC 3886,URL必须以方案开头(不限于http / https),例如:

  • www.example.com不是有效的URL(缺少方案)
  • javascript:void(0)是有效的URL,尽管不是HTTP URL
  • http://..是具有主机..的有效URL(是否解析取决于您的DNS)
  • https://example..com是有效的URL,与上述相同

20
@AshD 不是这样的;例如,你不能将 href 属性用作 <a> 标签。有效的 URL 必须以方案名称开头,例如 https://。(参考:https://tools.ietf.org/html/rfc3986#section-1.1.1) - Pavlo
8
新的 URL('javascript:alert(23)') - blade091
10
@Pavlo 这个语句返回true:isValidUrl("javascript:void(0)") - Praveena
6
我喜欢它能够教给我有关JS的新知识!据我所知,它没有假阴性。但是它确实存在一些假阳性:http://.. 或者 http:///a - aamarks
3
URL 在 Edge 上能正常工作,因此在它下面的所有内容可能不会按你的期望工作。请确保首先检查兼容性。 - Tony T.
显示剩余23条评论

409

一个带答案的相关问题

或者这个来自Devshed的正则表达式:

function validURL(str) {
  var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
    '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
    '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
    '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
    '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
    '(\\#[-a-z\\d_]*)?$','i'); // fragment locator
  return !!pattern.test(str);
}

4
无法处理有效的URL https://web.archive.org/web/20170817095211/https://github.com/Microsoft/vscode/issues/32405。 - George Mauer
2
www.jayakumar 是一个有效的URL吗?它返回true。 - Jayakumar
3
我认为这个答案已经过时了,建议不要在生产中使用它。 - David Constantine
6
如果我们将“111111111111111111111111111111111111111111111111”作为输入,服务器将会挂起。我遇到了这个问题,然后修改了我的代码为str.match(/(http(s)?://.)?(www.)?[-a-zA-Z0-9@:%._+#=]{2,256}.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.#?&//=]*)/g);。 - Epsi95
1
无法处理带有 @ 符号的链接,例如 https://medium.com/@User_name/ - Denis Petrov
显示剩余23条评论

165
function isURL(str) {
  var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
  '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|'+ // domain name
  '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
  '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
  '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
  '(\\#[-a-z\\d_]*)?$','i'); // fragment locator
  return pattern.test(str);
}

17
谷歌搜索图像链接无法使用:http://www.google.com/url?sa=i&rct=j&q=&esrc=s&source=images&cd=&docid=nIv5rk2GyP3hXM&tbnid=isiOkMe3nCtexM:&ved=0CAUQjRw&url=http%3A%2F%2Fanimalcrossing.wikia.com%2Fwiki%2FLion&ei=ygZXU_2fGKbMsQTf4YLgAQ&bvm=bv.65177938,d.aWc&psig=AFQjCNEpBfKnal9kU7Zu4n7RnEt2nerN4g&ust=1398298682009707 - bill davis
9
这个很慢,无法使用。 - Hernán Eche
10
你所说的“slow”是什么意思?start = new Date(); isURL("http://michalstefanow.com"); end = new Date(); diff = end - start; console.log(diff)我开了水壶,去了趟洗手间,打了个电话给妈妈,结果事情很快就完成了... - Mars Robertson
77
对于 aaa,它返回 true - alex naumov
4
这绝对不应该是正确答案。它无法通过许多测试用例,更重要的是,即使是一个短字符串也会卡住您的页面:isURL('12345678901234567890123') 添加一些更多字符会更糟。 - aamarks
显示剩余11条评论

116

与其使用正则表达式,我建议使用锚元素来实现。

当您设置 anchorhref 属性时,会设置各种其他属性。

var parser = document.createElement('a');
parser.href = "http://example.com:3000/pathname/?search=test#hash";

parser.protocol; // => "http:"
parser.hostname; // => "example.com"
parser.port;     // => "3000"
parser.pathname; // => "/pathname/"
parser.search;   // => "?search=test"
parser.hash;     // => "#hash"
parser.host;     // => "example.com:3000"

源代码

然而,如果绑定到href的值不是一个有效的URL,则这些辅助属性的值将为空字符串。

编辑:正如评论中指出的那样:如果使用无效的URL,可能会替换当前URL的属性。

因此,只要您没有传递当前页面的URL,就可以执行类似以下操作:

function isValidURL(str) {
   var a  = document.createElement('a');
   a.href = str;
   return (a.host && a.host != window.location.host);
}

8
这在Chrome 48中并非如此。如果传递给a.href的URL无效,则parser.host返回当前页面的主机名,而不是预期的false - Sam Beckham
3
哎呀!这太奇怪了。我发誓我测试过这个功能!我想可以说这个功能不会在当前页面上真正使用,所以可以直接更改条件语句。我会编辑这篇文章。 - Luke
这并不是一个非常典型的使用情况,但在Firefox浏览器窗口的上下文中,这种技术无法工作(对于插件开发非常重要)。 - chrmod
5
function isValidURL(str): 比使用正则表达式要好得多!谢谢! - Rodrigo
1
绕过这个问题的方法非常简单。不过这些属性是实验性的:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLAnchorElement - Boris D. Teoharov
显示剩余7条评论

103

我正在使用以下函数验证带有或不带有http/https的URL:

function isValidURL(string) {
  var res = string.match(/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g);
  return (res !== null)
};

var testCase1 = "http://en.wikipedia.org/wiki/Procter_&_Gamble";
console.log(isValidURL(testCase1)); // return true

var testCase2 = "http://www.google.com/url?sa=i&rct=j&q=&esrc=s&source=images&cd=&docid=nIv5rk2GyP3hXM&tbnid=isiOkMe3nCtexM:&ved=0CAUQjRw&url=http%3A%2F%2Fanimalcrossing.wikia.com%2Fwiki%2FLion&ei=ygZXU_2fGKbMsQTf4YLgAQ&bvm=bv.65177938,d.aWc&psig=AFQjCNEpBfKnal9kU7Zu4n7RnEt2nerN4g&ust=1398298682009707";
console.log(isValidURL(testCase2)); // return true

var testCase3 = "https://sdfasd";
console.log(isValidURL(testCase3)); // return false

var testCase4 = "dfdsfdsfdfdsfsdfs";
console.log(isValidURL(testCase4)); // return false

var testCase5 = "magnet:?xt=urn:btih:123";
console.log(isValidURL(testCase5)); // return false

var testCase6 = "https://stackoverflow.com/";
console.log(isValidURL(testCase6)); // return true

var testCase7 = "https://w";
console.log(isValidURL(testCase7)); // return false

var testCase8 = "https://sdfasdp.ppppppppppp";
console.log(isValidURL(testCase8)); // return false


2
看起来是个不错的解决方案!你能否添加一些测试,以展示它在某些边缘情况下的工作情况(例如,请参见这些评论)? - Basj
@aamarks,我检查了你的答案。你的答案在https://sdfasdp.ppppppppppp这个链接上失败了,即返回了true,但我的答案返回了false,我认为这是预期的结果。 - Vikasdeep Singh
5
对于sadf@gmail.com,它返回的是true...是否应该这样?我想不应该。 - Zohab Ali
2
当URL带有端口时,这将失败。 - Wedava
1
这段代码未通过协议验证。看一下: url = 'htt1ps://googl1e.com' console.log(url + ' -> ' + isValidURL(url)) - Chirag Visavadiya
显示剩余10条评论

52

下面展示如何使用JavaScript验证URL

function ValidURL(str) {
  var regex = /(?:https?):\/\/(\w+:?\w*)?(\S+)(:\d+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/;
  if(!regex .test(str)) {
    alert("Please enter valid URL.");
    return false;
  } else {
    return true;
  }
}

"mailto:regaltheme@email.com" 这是一个链接,但是你的代码无法工作。我该如何修复? - Tu Le Anh
使用httphttps的URL仅用于Web地址。还有许多其他不使用这些方案的URL,它们也是有效的。 - Suncat2000

31

2
这个问题让我很困扰,因为有些奇怪的URL实际上是由浏览器解析的,例如:在URL中包含{ - Willyfrog

27

对已接受答案的改进...

  • 检查ftp/ftps协议
  • 对反斜杠(\\)进行双重转义
  • 确保域名有一个点和一个扩展名(.com .io .xyz)
  • 允许路径中包含冒号(:),例如:http://thingiverse.com/download:1894343
  • 允许路径中包含“&”符号,例如:http://en.wikipedia.org/wiki/Procter_&_Gamble
  • 允许路径中包含“@”符号,例如:https://medium.com/@techytimo

    isURL(str) {
      var pattern = new RegExp('^((ft|htt)ps?:\\/\\/)?'+ // protocol
      '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name and extension
      '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
      '(\\:\\d+)?'+ // port
      '(\\/[-a-z\\d%@_.~+&:]*)*'+ // path
      '(\\?[;&a-z\\d%@_.,~+&:=-]*)?'+ // query string
      '(\\#[-a-z\\d_]*)?$','i'); // fragment locator
      return pattern.test(str);
    }
    

6
不,它不应该成为被接受的答案。像其他一些答案一样,它仅仅依赖于一个只有33个字符的字符串:isURL('123456789012345678901234567890123'),并且在许多边缘测试案例中失败了:http://foo.com/blah_blah_(wikipedia)_(again) //错误地返回false。 - aamarks
2
那是因为localhost:8080不是一个有效的URL。 - Shanerk
1
工作示例:https://runkit.com/shanekenyon87/5bc0e57263c77b0012db05dc - Shanerk
1
似乎不起作用:在输入较长的情况下卡住了(就像@aanmarks说的那样) - cecemel
如果你想捕捉不完整的链接,例如 https://www.test,你可能需要添加以下代码: if(str.indexOf('www.')>-1 && (str.split('.').length - 1)<2) { return false; } - Avatar
显示剩余3条评论

24
你可以使用URL 原生 API
  const isUrl = string => {
      try { return Boolean(new URL(string)); }
      catch(e){ return false; }
  }

4
看起来与 @pavlo 提供的答案非常相似,只是变量名改了一下 ;) - Munim Munna
2
现在应该有一个简单的本地方法来检查这个问题 - 这个答案看起来非常有前途,但正如@Basj上面提到的那样,它会早早地返回true。 - zero_cool

19

使用validator.js

ES6

import isURL from 'validator/lib/isURL'

isURL(string)

没有 ES6

var validator = require('validator');

validator.isURL(string)

您还可以通过将可选的 options 对象作为 isURL 的第二个参数传递来微调此函数的行为。

这是默认的 options 对象:

let options = {
    protocols: [
        'http',
        'https',
        'ftp'
    ],
    require_tld: true,
    require_protocol: false,
    require_host: true,
    require_valid_protocol: true,
    allow_underscores: false,
    host_whitelist: false,
    host_blacklist: false,
    allow_trailing_dot: false,
    allow_protocol_relative_urls: false,
    disallow_auth: false
}

isURL(string, options)

host_whitelisthost_blacklist可以是一组主机的数组,还支持正则表达式。

let options = {
    host_blacklist: ['foo.com', 'bar.com'],
}

isURL('http://foobar.com', options) // => true
isURL('http://foo.bar.com/', options) // => true
isURL('http://qux.com', options) // => true

isURL('http://bar.com/', options) // => false
isURL('http://foo.com/', options) // => false


options = {
    host_blacklist: ['bar.com', 'foo.com', /\.foo\.com$/],
}

isURL('http://foobar.com', options) // => true
isURL('http://foo.bar.com/', options) // => true
isURL('http://qux.com', options) // => true

isURL('http://bar.com/', options) // => false
isURL('http://foo.com/', options) // => false
isURL('http://images.foo.com/', options) // => false
isURL('http://cdn.foo.com/', options) // => false
isURL('http://a.b.c.foo.com/', options) // => false

5
很不错!这个小型库(压缩后少于40k)非常受欢迎(每周在npm上下载量超过3M),可以为您的特定用例指定URL的有效性并提供许多灵活性,除了URL之外还有其他一些验证器。在我看来,这绝对是最好的答案。 - Javid Jamae
1
不错的库。它可以验证URL,还有许多其他功能。 - Bemipefe
1
这对我在创建New Relic Synthetics脚本时非常有用。非常实用。 - Paul Wenzel
1
迄今为止最好的解决方案 - Luciano Fantuzzi
对不起,@JavidJamae?当"压缩"时,40K是一个小型的库吗?有一个大小不到100K的3D视频游戏.kkrieger ^^ - Artfaith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接