检查JavaScript字符串是否为URL


283

JavaScript中是否可以检查字符串是否为URL?

RegExes被排除在外,因为URL的写法很可能像stackoverflow;也就是说,它可能没有.comwww或者http


22
如果缺少http,则默认情况下没有网址。
nfechner 2011年

1
@nfechner就是说,如果它没有指定协议并且使用冒号字符(最好是下一个两个正斜杠),那么它不是URL吗?
jcolebrand

5
正如您可以在URL RFC中阅读的那样,使String成为有效URL的实际上唯一必要的部分是冒号。有效网址如下:<scheme>:<scheme-specific-part>
nfechner


8
您如何测试某物是否为URL 高度依赖于上下文,并且如果没有进一步的限定,也太含糊。对您是否重要,是否符合URL RFC规范,在OS系统调用中打开URLhref时起作用,在锚元素中解析为,在调用时起作用window.open(url),指向确实存在的东西,在浏览器位置中起作用酒吧,还是以上的组合?根据您关心的答案,您将获得截然不同的答案。
罗伊·廷克

Answers:


188

一个与答案相关的问题:

JavaScript正则表达式网址匹配

或者来自Devshed的 Regexp :

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

1
我知道,但是我正在搜索书签,并且大多数书签都像stackoverflow一样写(没有.com等)
Bruno

3
@Bruno:很有可能它们在内部使用单独的标题和URL保存,例如{ title: "Stackoverflow", uri: "http://stackoverflow.com" } Update:的确,请参见code.google.com/chrome/extensions/bookmarks.html
Marcel Korpel 2011年

10
尝试使用您的示例。但是我在萤火虫上说了一个错误invalid quantifier。任何想法?
西西尔(Sisir)2012年

125
函数返回:SyntaxError: Invalid regular expression: /^(https?://)?((([a-zd]([a-zd-]*[a-zd])*).)+[a-z]{2,}|((d{1,3}.){3}d{1,3}))(:d+)?(/[-a-zd%_.~+]*)*(?[;&a-zd%_.~+=-]*)?(#[-a-zd_]*)?$/: Invalid group Google Chrome(版本30.0.1599.101)(Mac OS X:10.8.5)
dr.dimitru

10
请注意,如果将字符串用作参数,则RegExp必须加倍转义反斜杠-否则会出现诸如无效组之类的错误
Kjell 2015年

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

13
google搜索图像链接失败: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
戴维斯

7
这是不可用的慢
埃尔南Eche

3
@HernánEche你这么意味着什么?start = new Date(); isURL("http://michalstefanow.com"); end = new Date(); diff = end - start; console.log(diff)我放上水壶,上了厕所,叫我妈妈,事情很快就完成了……
火星罗伯逊

62
它返回trueaaa
Alex naumov '16

1
这绝对不是正确的答案。它无法通过许多测试用例,更重要的是,它会将页面挂在一个短字符串上:isURL('12345678901234567890123')添加更多字符,甚至更糟。
aamarks

142

您可以尝试使用URL构造函数:如果不抛出,则该字符串是有效的URL:

function isValidUrl(string) {
  try {
    new URL(string);
  } catch (_) {
    return false;  
  }

  return true;
}

术语“ URL”在RFC 3886中定义为URI。它必须以方案名称开头,并且方案名称不限于http / https。

著名的例子:

  • www.google.com 网址无效(丢失方案)
  • javascript:void(0) 是有效的网址,尽管不是HTTP网址
  • http://..是有效的URL,主机..; 是否解析取决于您的DNS
  • https://google..com 是有效的网址,与上面相同

如果要检查字符串是否为有效的HTTP URL:

function isValidHttpUrl(string) {
  let url;

  try {
    url = new URL(string);
  } catch (_) {
    return false;  
  }

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

13
@AshD不,不是;例如,您不能将用作href属性<a>。有效URL 必须以方案名称开头,例如https://
巴甫洛

3
新网址('javascript:alert(23)')
blade091

6
@Pavlo,这将返回trueisValidUrl("javascript:void(0)")
Praveena '17

3
我喜欢这样教我有关js的新知识!我没有发现任何假阴性。它确实有一些误报:http://..http:///a
aamarks

2
URL从Edge开始运行,因此其下方的所有内容均可能无法正常运行。确保首先检查兼容性。
Tony T.

97

建议不要使用正则表达式,而建议使用锚元素。

设置的href属性时,还会设置anchor其他各种属性。

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

3
事实并非如此(至少在Chrome 48中如此)。如果传递给的网址a.href无效,则parser.host返回您当前所在页面的主机名,而不是预期的false
山姆·贝克汉姆,

2
加!那真是怪了。我发誓我已经测试过了!我认为可以说确实不需要在当前页面上使用,因此可以更改条件。我将编辑帖子。
路加福音

这不是一个非常典型的用例,但是此技术在Firefox浏览器窗口(对于插件开发很重要)的上下文中不起作用
chrmod

@SamBeckham在使用此方法时,这绝对是一个问题,但是我只想指出这不是一种特殊的行为。如果页面上的链接无效,例如<a href="invalidurl">,则该链接转到您的域。它被添加到当前URL的末尾。因此,Chrome通过为您提供来自“解析器”元素的当前主机名,从而可以正确地完成工作。
青训营

4
function isValidURL(str):比使用正则表达式好得多!谢谢!
罗德里戈

46

我正在使用以下功能来验证带有或不带有URL http/https

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

@Basj添加了测试用例。请检查
Vikasdeep Singh

不错,不能通过http://⌘.ws或142.42.1.1,并且允许http://.www.foo.bar./,但它不会像其他正则表达式一样挂起,包括评分最高的答案。
aamarks

@aamarks我检查了您的答案。您的答案失败了,例如https://sdfasdp.ppppppppppp返回,true但是false我认为这是我的返回。
Vikasdeep Singh '18

4
它的返回真值sadf@gmail.com……应该吗?我猜应该不会
Zohab Ali

35

使用JavaScript验证网址如下所示

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

3
正则表达式的几个部分可以大大减小:1)(http|https)(?:https?); B):{0,1}:?; c)[0-9]\d
德米特里·帕尔


23

改进已接受答案...

  • 检查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);
    }

5
不,它不应该是公认的答案。像其他一些字符串一样,它仅停留在33个字符串上:isURL('123456789012345678901234567890123')并在许多边缘案例测试中失败:foo.com/blah_blah_ (wikipedia)_(再次 //错误地返回false。
aamarks

2
那是因为localhost:8080不是有效的URL。
Shane


应该是ftps:// localhost:8080 =)
vp_arth

它似乎不起作用:挂在长输入上(如@aanmarks所说)
cecemel

13

这是另一种方法。

var elm;
function isValidURL(u){
  if(!elm){
    elm = document.createElement('input');
    elm.setAttribute('type', 'url');
  }
  elm.value = u;
  return elm.validity.valid;
}

console.log(isValidURL('http://www.google.com/'));
console.log(isValidURL('//google.com'));
console.log(isValidURL('google.com'));
console.log(isValidURL('localhost:8000'));


教育法规!这里的机制可能与new URL(string)Pavlo代码中的工作方式相同。在我测试的所有边缘情况下,两种测试的结果均相同。我喜欢他的代码,因为它更简单并且不涉及创建元素,但是您的代码却快了好几倍(可能是因为它在第一次使用后没有创建el)。
aamarks

1
谢谢!我执行了您的建议。但是请注意:较旧的浏览器和/或移动设备WebView可能未实现<input type = url>元素;因此,输入值将像普通文本一样对待(不进行URL验证)。参考:developer.mozilla.org/en-US/docs/Web/HTML/Element/input/url
Panini Luncher

10

(我没有代表对ValidURL示例发表评论;因此请将此作为答复。)

虽然不鼓励使用协议相对URL(相对于协议URL),但有时确实会使用它们。为了使用正则表达式验证此类URL,协议部分可以是可选的,例如:

function isValidURL(str) {
    var pattern = new RegExp('^((https?:)?\\/\\/)?'+ // protocol
        '(?:\\S+(?::\\S*)?@)?' + // authentication
        '((([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 locater
    if (!pattern.test(str)) {
        return false;
    } else {
        return true;
    }
}

正如其他人指出的那样,正则表达式似乎并不是最适合验证URL的方法。


我本来以为这很好,但是它在mathiasbynens.be/demo/url-regex的许多测试中都失败了,然后挂起了isValidURL("https://d1f4470da51b49289906b3d6cbd65074@app.getsentry.com/13176")
aamarks

是的,就像我说的,我只是在协议部分进行了评论。我添加了authentication子句来处理@。它没有挂在我的浏览器中
ko la

抱歉,我正在对其中的几个进行评估,却错过了您对给定答案的评论。我认为您的更正甚至在我第一次访问此页面时帮助我开始了这些工作。现在不挂。
aamarks '18

9

您可以使用URL本机API

  const isUrl = string => {
      try { return Boolean(new URL(string)); }
      catch(e){ return false; }
  }

3
看起来非常类似于@pavlo提供的答案,唯一的变量名称更改;)
穆奈姆Munna

2
到目前为止,确实应该有一个简单的本机方法来检查此问题-这个答案看起来很有希望,但早在上述@Basj时就返回了true。
zero_cool

8

如前所述,完美的正则表达式是难以捉摸的,但似乎仍然是一种合理的方法(替代方案是服务器端测试或新的实验性URL API)。但是,对于通用网址,排名靠前的答案通常会返回false,但更糟糕的是,即使是像这样的简单字符串,它也会冻结您的应用程序/页面几分钟isURL('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')。一些评论中已经指出了这一点,但是很可能并没有看到不好的输入。这样的挂起使该代码无法在任何严肃的应用程序中使用。我认为这是由于代码中重复出现的不区分大小写的集合所致((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|' ...。取出“ i”,它不会挂起,但是当然不能按预期工作。但是,即使使用了忽略大小写标志,那些测试也会拒绝允许的高unicode值。

已经提到的最好的是:

function isURL(str) {
  return /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/.test(str); 
}

这来自Github segmentio / is-url。关于代码存储库的好处是,您可以看到测试和任何问题,以及贯穿其中的测试字符串。有一个分支允许字符串缺少协议,例如google.com,尽管您当时可能做过多的假设。存储库已更新,我不打算尝试在此处保留镜像。为了避免RegEx 重做可被DOS攻击利用,已将其分为多个测试(我不必担心客户端js会担心这一点,但是您确实需要担心页面挂了这么长时间以至于访客离开您的网站)。

我见过另外一个存储库,它可能在dperini / regex-weburl.js上对isURL可能更好,但是它非常复杂。它具有更大的有效和无效URL测试列表。上面简单的一个仍然可以传递所有正数,并且只能阻止一些奇数负数http://a.b--c.de/以及特殊ip。

无论您选择哪种方式,都可以使用浏览器的“开发人员工具”检查器,通过我根据dperini / regex-weburl.js上的测试改编的此功能来运行它。

function testIsURL() {
//should match
console.assert(isURL("http://foo.com/blah_blah"));
console.assert(isURL("http://foo.com/blah_blah/"));
console.assert(isURL("http://foo.com/blah_blah_(wikipedia)"));
console.assert(isURL("http://foo.com/blah_blah_(wikipedia)_(again)"));
console.assert(isURL("http://www.example.com/wpstyle/?p=364"));
console.assert(isURL("https://www.example.com/foo/?bar=baz&inga=42&quux"));
console.assert(isURL("http://✪df.ws/123"));
console.assert(isURL("http://userid:password@example.com:8080"));
console.assert(isURL("http://userid:password@example.com:8080/"));
console.assert(isURL("http://userid@example.com"));
console.assert(isURL("http://userid@example.com/"));
console.assert(isURL("http://userid@example.com:8080"));
console.assert(isURL("http://userid@example.com:8080/"));
console.assert(isURL("http://userid:password@example.com"));
console.assert(isURL("http://userid:password@example.com/"));
console.assert(isURL("http://142.42.1.1/"));
console.assert(isURL("http://142.42.1.1:8080/"));
console.assert(isURL("http://➡.ws/䨹"));
console.assert(isURL("http://⌘.ws"));
console.assert(isURL("http://⌘.ws/"));
console.assert(isURL("http://foo.com/blah_(wikipedia)#cite-1"));
console.assert(isURL("http://foo.com/blah_(wikipedia)_blah#cite-1"));
console.assert(isURL("http://foo.com/unicode_(✪)_in_parens"));
console.assert(isURL("http://foo.com/(something)?after=parens"));
console.assert(isURL("http://☺.damowmow.com/"));
console.assert(isURL("http://code.google.com/events/#&product=browser"));
console.assert(isURL("http://j.mp"));
console.assert(isURL("ftp://foo.bar/baz"));
console.assert(isURL("http://foo.bar/?q=Test%20URL-encoded%20stuff"));
console.assert(isURL("http://مثال.إختبار"));
console.assert(isURL("http://例子.测试"));
console.assert(isURL("http://उदाहरण.परीक्षा"));
console.assert(isURL("http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com"));
console.assert(isURL("http://1337.net"));
console.assert(isURL("http://a.b-c.de"));
console.assert(isURL("http://223.255.255.254"));
console.assert(isURL("postgres://u:p@example.com:5702/db"));
console.assert(isURL("https://d1f4470da51b49289906b3d6cbd65074@app.getsentry.com/13176"));

//SHOULD NOT MATCH:
console.assert(!isURL("http://"));
console.assert(!isURL("http://."));
console.assert(!isURL("http://.."));
console.assert(!isURL("http://../"));
console.assert(!isURL("http://?"));
console.assert(!isURL("http://??"));
console.assert(!isURL("http://??/"));
console.assert(!isURL("http://#"));
console.assert(!isURL("http://##"));
console.assert(!isURL("http://##/"));
console.assert(!isURL("http://foo.bar?q=Spaces should be encoded"));
console.assert(!isURL("//"));
console.assert(!isURL("//a"));
console.assert(!isURL("///a"));
console.assert(!isURL("///"));
console.assert(!isURL("http:///a"));
console.assert(!isURL("foo.com"));
console.assert(!isURL("rdar://1234"));
console.assert(!isURL("h://test"));
console.assert(!isURL("http:// shouldfail.com"));
console.assert(!isURL(":// should fail"));
console.assert(!isURL("http://foo.bar/foo(bar)baz quux"));
console.assert(!isURL("ftps://foo.bar/"));
console.assert(!isURL("http://-error-.invalid/"));
console.assert(!isURL("http://a.b--c.de/"));
console.assert(!isURL("http://-a.b.co"));
console.assert(!isURL("http://a.b-.co"));
console.assert(!isURL("http://0.0.0.0"));
console.assert(!isURL("http://10.1.1.0"));
console.assert(!isURL("http://10.1.1.255"));
console.assert(!isURL("http://224.1.1.1"));
console.assert(!isURL("http://1.1.1.1.1"));
console.assert(!isURL("http://123.123.123"));
console.assert(!isURL("http://3628126748"));
console.assert(!isURL("http://.www.foo.bar/"));
console.assert(!isURL("http://www.foo.bar./"));
console.assert(!isURL("http://.www.foo.bar./"));
console.assert(!isURL("http://10.1.1.1"));}

然后测试“ a”的字符串。

在发布看似很棒的正则表达式之前,请参阅Mathias Bynens对isURL regex进行的比较,以获取更多信息。


我检查了你的答案。您的答案无法满足sdfasdp.ppppppppppppp的要求,即返回true但预期为false
Vikasdeep Singh 18-4-17的

1
从结构上来说,我认为这是一个有效的URL。不是标准专家,但我认为.com部分的长度没有限制(我知道.online是合法的)。
aamarks

1
几个月前,我几乎不知道如何编写正则表达式。问题很严重。我引用的两个正则表达式都可以完成isURL('a'.repeat(100))数百万次/秒(来自dperini的更复杂的速度实际上更快)。([a-zA-Z] +)*形式的某些高阶答案将需要几个小时才能完成一次。查找RegEx重做以获取更多信息。
aamarks

6

我无法评论离#5717133最近的帖子,但是下面是我弄清楚如何使@ tom-gullen regex工作的方式。

/^(https?:\/\/)?((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|((\d{1,3}\.){3}\d{1,3}))(\:\d+)?(\/[-a-z\d%_.~+]*)*(\?[;&a-z\d%_.~+=-]*)?(\#[-a-z\d_]*)?$/i

2
这对我有用,但是我需要反斜杠。var pattern = new RegExp('(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$', 'i');
费尔南多·查韦斯·埃雷拉,

检查w3resource.com/javascript-exercises/…了解更多测试用例
Kewal Shah,

5

使用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_whitelist并且host_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

1
真好!小型库(最小库少于4万),流行库(每周在npm上超过3M的下载量)为您指定特定用例的URL有效性提供了极大的灵活性,并且除URL外还有许多其他验证器。到目前为止,这是最好的答案,恕我直言。
Javid Jamae

4

我用来验证URL“字符串”的一个函数是:

var matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/;

function isUrl(string){
  return matcher.test(string);
}

该函数将返回一个布尔值,无论字符串是否是URL。

例子:

isUrl("https://google.com");     // true
isUrl("http://google.com");      // true
isUrl("http://google.de");       // true
isUrl("//google.de");            // true
isUrl("google.de");              // false
isUrl("http://google.com");      // true
isUrl("http://localhost");       // true
isUrl("https://sdfasd");         // false

4

使用纯正则表达式很难做到这一点,因为URL有很多“不便”。

  1. 例如,域名对连字符有复杂的限制:

    一个。中间可以有许多连续的连字符。

    b。但是域名的第一个字符和最后一个字符不能为连字符

    C。第三个和第四个字符不能都是连字符

  2. 同样,端口号只能在1-65535范围内。如果您提取端口部分并将其转换为,这很容易检查,int但是使用正则表达式很难检查。

  3. 也没有简单的方法来检查有效的域扩展名。一些国家/地区具有二级域名(例如'co.uk'),或者扩展名可以是一个长词,例如'.international'。并定期添加新的TLD。此类内容只能通过硬编码列表进行检查。(请参阅https://en.wikipedia.org/wiki/Top-level_domain

  4. 然后是磁铁URL,FTP地址等。这些都有不同的要求。

尽管如此,这是一个处理几乎所有事情的函数,除了:

  • 情况1. c
  • 接受任何1-5位数字的端口号
  • 接受任何2-13个字符的扩展名
  • 不接受ftp,磁铁等...

function isValidURL(input) {
    pattern = '^(https?:\\/\\/)?' + // protocol
        '((([a-zA-Z\\d]([a-zA-Z\\d-]{0,61}[a-zA-Z\\d])*\\.)+' + // sub-domain + domain name
        '[a-zA-Z]{2,13})' + // extension
        '|((\\d{1,3}\\.){3}\\d{1,3})' + // OR ip (v4) address
        '|localhost)' + // OR localhost
        '(\\:\\d{1,5})?' + // port
        '(\\/[a-zA-Z\\&\\d%_.~+-:@]*)*' + // path
        '(\\?[a-zA-Z\\&\\d%_.,~+-:@=;&]*)?' + // query string
        '(\\#[-a-zA-Z&\\d_]*)?$'; // fragment locator
    regex = new RegExp(pattern);
    return regex.test(input);
}

let tests = [];
tests.push(['', false]);
tests.push(['http://en.wikipedia.org/wiki/Procter_&_Gamble', true]);
tests.push(['https://sdfasd', false]);
tests.push(['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', true]);
tests.push(['https://stackoverflow.com/', true]);
tests.push(['https://w', false]);
tests.push(['aaa', false]);
tests.push(['aaaa', false]);
tests.push(['oh.my', true]);
tests.push(['dfdsfdsfdfdsfsdfs', false]);
tests.push(['google.co.uk', true]);
tests.push(['test-domain.MUSEUM', true]);
tests.push(['-hyphen-start.gov.tr', false]);
tests.push(['hyphen-end-.com', false]);
tests.push(['https://sdfasdp.international', true]);
tests.push(['https://sdfasdp.pppppppp', false]);
tests.push(['https://sdfasdp.ppppppppppppppppppp', false]);
tests.push(['https://sdfasd', false]);
tests.push(['https://sub1.1234.sub3.sub4.sub5.co.uk/?', true]);
tests.push(['http://www.google-com.123', false]);
tests.push(['http://my--testdomain.com', false]);
tests.push(['http://my2nd--testdomain.com', true]);
tests.push(['http://thingiverse.com/download:1894343', true]);
tests.push(['https://medium.com/@techytimo', true]);
tests.push(['http://localhost', true]);
tests.push(['localhost', true]);
tests.push(['localhost:8080', true]);
tests.push(['localhost:65536', true]);
tests.push(['localhost:80000', false]);
tests.push(['magnet:?xt=urn:btih:123', true]);

for (let i = 0; i < tests.length; i++) {
    console.log('Test #' + i + (isValidURL(tests[i][0]) == tests[i][1] ? ' passed' : ' failed') + ' on ["' + tests[i][0] + '", ' + tests[i][1] + ']');
}


1

我认为使用本地URL API优于@pavlo建议的复杂正则表达式模式。它有一些缺点,但是我们可以通过一些额外的代码来解决。对于以下有效网址,此方法将失败。

//cdn.google.com/script.js

我们可以预先添加缺少的协议以避免这种情况。它还无法检测到以下无效的URL。

http://w
http://..

那么,为什么要检查整个网址?我们可以只检查域。我借用了正则表达式从这里验证域。

function isValidUrl(string) {
    if (string && string.length > 1 && string.slice(0, 2) == '//') {
        string = 'http:' + string; //dummy protocol so that URL works
    }
    try {
        var url = new URL(string);
        return url.hostname && url.hostname.match(/^([a-z0-9])(([a-z0-9-]{1,61})?[a-z0-9]{1})?(\.[a-z0-9](([a-z0-9-]{1,61})?[a-z0-9]{1})?)?(\.[a-zA-Z]{2,4})+$/) ? true : false;
    } catch (_) {
        return false;
    }
}

hostname属性是的空字符串javascript:void(0),因此也适用于此,您也可以添加IP地址验证程序。我最想坚持使用本机API,并希望它会在不久的将来开始支持所有功能。


有趣,但可能仍需要处理正则表达式,因为它现在引入了假阴性,这new URL在我完成的测试中没有。这是在调用:http://142.42.1.1 //false并阻止高unicode字符串。
aamarks

1

该问题要求一种验证方法来查询url之类的网址stackoverflow,而该协议中没有协议或主机名中没有任何点。因此,验证URL sintax不是问题,而是通过实际调用来检查它是否为有效URL。

我尝试了几种方法来了解url是否存在并且可以从浏览器中调用,但是没有找到任何方法来使用javascript测试调用的响应标头:

  • 添加锚点元素可以触发该click()方法。
  • 使用具有挑战性的url进行ajax调用'GET'是可以的,但是由于CORS策略的缘故,它具有各种限制,而不是使用的情况ajax,因为url可能是服务器域之外的任何URL。
  • 使用fetch API的解决方法类似于ajax。
  • 另一个问题是我的服务器处于https协议之下,并且在调用非安全网址时会引发异常。

因此,我能想到的最好的解决方案是CURL使用javascript尝试一些工具来执行某些工具curl -I <url>。不幸的是,我没有找到任何东西,而且看起来不可能。我对此表示感谢。

但是,最后,我运行了一个服务器PHP,由于几乎所有请求都使用Ajax,因此我在服务器端编写了一个函数,以在其中执行curl请求并返回浏览器。

关于“ stackoverflow”问题上的单个网址,它将带我到https://daniserver.com.ar/stackoverflow,daniserver.com.ar是我自己的域。


OP可能已经表明了他的意图。当然,问题会因您的需要而异,排除误报还是包括误报是否更为重要。正如问题所述,我似乎没有任何答案。您真的可以接受foo并假设它是http或https或.com或.es或不计其数的后缀吗?您是否一直向厨房扔水槽,直到得到正确的结果?
aamarks

1

这似乎是CS中最困难的问题之一;)

这是另一个不完整的解决方案,对我来说效果很好,并且比我在这里看到的其他解决方案更好。我为此使用input [type = url]来支持IE11,否则使用window.URL来执行验证会更加简单:

const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
function isValidIpv4(ip) {
  if (!ipv4Regex.test(ip)) return false;
  return !ip.split('.').find(n => n > 255);
}

const domainRegex = /(?:[a-z0-9-]{1,63}\.){1,125}[a-z]{2,63}$/i;
function isValidDomain(domain) {
  return isValidIpv4(domain) || domainRegex.test(domain);
}

let input;
function validateUrl(url) {
  if (! /^https?:\/\//.test(url)) url = `http://${url}`; // assuming Babel is used
  // to support IE11 we'll resort to input[type=url] instead of window.URL:
  // try { return isValidDomain(new URL(url).host) && url; } catch(e) { return false; }
  if (!input) { input = document.createElement('input'); input.type = 'url'; }
  input.value = url;
  if (! input.validity.valid) return false;
  const domain = url.split(/^https?:\/\//)[1].split('/')[0].split('@').pop();
  return isValidDomain(domain) && url;
}

console.log(validateUrl('google'), // false
  validateUrl('user:pw@mydomain.com'),
  validateUrl('https://google.com'),
  validateUrl('100.100.100.100/abc'),
  validateUrl('100.100.100.256/abc')); // false

为了接受不完整的输入(例如“ www.mydomain.com”),如果在这些情况下协议为“ http”,它还将使其有效,如果地址有效,则返回有效的URL。无效时返回false。

它还支持IPv4域,但不支持IPv6。


1

在我的情况下,我唯一的要求是,将用户输入放置在标签的href中时,不会将用户输入解释为相对链接,并且此处的答案要么是OTT,要么是所允许的URL不符合我的要求,因此我要用的是:

^https?://.+$

不用正则表达式就可以很容易地实现同一件事。


1

这和我一起工作

function isURL(str) {
  var regex = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/;
  var pattern = new RegExp(regex); 
return pattern.test(str);
}

1
答案已经在4年前由kavitha Reddy给出。
aamarks

我只是变得更加简单和抽象
HeshamSalama

1

如果您可以更改输入类型,我认为此解决方案会容易得多:

您可以type="url"在输入中简单使用,并checkValidity()在js中进行检查

例如:

your.html

<input id="foo" type="url">

your.js

// The selector is JQuery, but the function is plain JS
$("#foo").on("keyup", function() {
    if (this.checkValidity()) {
        // The url is valid
    } else {
        // The url is invalid
    }
});

1

显然,这不是最有效的方法,但是它易于阅读,并且易于形成您所需的任何内容。从这里添加正则表达式/复杂性会更容易。所以这是一个非常务实的方法

const validFirstBits = ["ftp://", "http://", "https://", "www."];
const invalidPatterns = [" ", "//.", ".."];

export function isUrl(word) {
// less than www.1.dk
if (!word || word.length < 8) return false;

// Let's check and see, if our candidate starts with some of our valid first bits
const firstBitIsValid = validFirstBits.some(bit => word.indexOf(bit) === 0);
if (!firstBitIsValid) return false;

const hasInvalidPatterns = invalidPatterns.some(
    pattern => word.indexOf(pattern) !== -1,
);

if (hasInvalidPatterns) return false;

const dotSplit = word.split(".");
if (dotSplit.length > 1) {
    const lastBit = dotSplit.pop(); // string or undefined
    if (!lastBit) return false;
    const length = lastBit.length;
    const lastBitIsValid =
        length > 1 || (length === 1 && !isNaN(parseInt(lastBit)));
    return !!lastBitIsValid;
}

    return false;
}

测试:

import { isUrl } from "./foo";

describe("Foo", () => {
    test("should validate correct urls correctly", function() {
        const validUrls = [
            "http://example.com",
            "http://example.com/blah",
            "http://127.0.0.1",
            "http://127.0.0.1/wow",
            "https://example.com",
            "https://example.com/blah",
            "https://127.0.0.1:1234",
            "ftp://example.com",
            "ftp://example.com/blah",
            "ftp://127.0.0.1",
            "www.example.com",
            "www.example.com/blah",
        ];

        validUrls.forEach(url => {
            expect(isUrl(url) && url).toEqual(url);
        });
    });

    test("should validate invalid urls correctly", function() {
        const inValidUrls = [
            "http:// foo.com",
            "http:/foo.com",
            "http://.foo.com",
            "http://foo..com",
            "http://.com",
            "http://foo",
            "http://foo.c",
        ];

        inValidUrls.forEach(url => {
            expect(!isUrl(url) && url).toEqual(url);
        });
    });
});

1

Mathias Bynens已编译了包含测试URL 的知名URL正则表达式列表。没有什么理由写一个新的正则表达式。只需选择一个最适合您的现有。

但是,这些正则表达式的比较表还显示,几乎不可能使用单个正则表达式进行URL验证。Bynens列表中的所有正则表达式都会产生误报和误报。

我建议您使用现有的URL解析器(例如,new URL('http://www.example.com/')在JavaScript中),然后对要解析的URL解析形式和规范化形式应用要执行的检查。它的组件。使用JavaScript URL界面的另一个好处是,它将仅接受浏览器真正接受的此类URL。

您还应该记住,技术上不正确的URL仍然可以使用。例如http://w_w_w.example.com/http://www..example.com/http://123.example.com/都有一个无效的主机名的一部分,但每次我都知道浏览器将尝试打开它们无投诉,当你在这些无效名称指定IP地址/etc/hosts/等的URL,甚至会工作,但只在您的计算机上。

因此,问题不仅仅在于URL是否有效,而在于在特定上下文中哪些URL有效并且应该被允许。

如果您想进行URL验证,那么有很多细节和边缘情况很容易忽略:

  • 网址可能包含中的凭据http://user:password@www.example.com/
  • 端口号必须在0-65535的范围内,但是您可能仍要排除通配符端口0。
  • http://www.example.com:000080/一样,端口号可能带有前导零。
  • IPv4地址绝不限于0-255范围内的4个十进制整数。您可以使用1-4个整数,它们可以是十进制,八进制或十六进制。该网址的https://010.010.000010.010/HTTPS://0x8.0x8.0x0008.0x8/HTTPS://8.8.2056/HTTPS://8.526344/https://开头134744072 /都是有效的,只是创造性的写作方式https://8.8.8.8/
  • 允许回送地址(http://127.0.0.1/),专用IP地址(http://192.168.1.1),本地链接地址(http://169.254.100.200)等可能会影响安全性或隐私。例如,如果您允许它们作为论坛中用户头像的地址,则会导致用户的浏览器在其本地网络和物联网中发送未经请求的网络请求,这样的请求可能会导致有趣而又不太有趣的事情。发生在你家里。
  • 出于相同的原因,您可能希望放弃指向不完全限定的主机名的链接,换句话说,就是没有点的主机名。
  • 但是主机名可能总是带有尾随点(如中的http://www.stackoverflow.com.)。
  • 链接的主机名部分可能包含IPv6地址的尖括号,如http:// [:: 1]所示
  • IPv6地址也具有专用网络或链接本地地址等的范围。
  • 如果您阻止某些IPv4地址,请记住,例如https://127.0.0.1https:// [:: ffff:127.0.0.1]指向同一资源(如果计算机的回送设备已准备好IPv6) )。
  • URL的主机名部分现在可能包含Unicode,因此字符范围[-0-9a-zA-z]肯定不再足够。
  • 许多顶级域的注册表都定义了特定的限制,例如在允许的Unicode字符集上。或者,它们可以细分其名称空间(例如co.uk和许多其他名称空间)。
  • 顶级域不得包含十进制数字,除非IDN A标签前缀为“ xn--”,否则不允许使用连字符。
  • Unicode顶级域(及其带有“ xn--”的punycode编码)仍必须仅包含字母,但是谁想在正则表达式中进行检查?

这些限制和规则中的哪个适用于项目要求和口味。

我最近为Web应用程序编写了URL验证器,该验证器适用于论坛,社交网络等中用户提供的URL。随意将其用作自己的基础:

我还写了一篇博客文章《 URL验证的血腥细节》,其中包含更深入的信息。


1

我将函数更改为Match +,并在此处使用斜杠及其工作进行更改:(http://和https)

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

0

这只是一个非常简单的检查,以确保存在有效的协议,并且域扩展名必须是两个或更多字符。

is_valid_url = ( $url ) => {

    let $url_object = null;

    try {
        $url_object = new URL( $url );
    } catch ( $error ) {
        return false;
    }

    const $protocol = $url_object.protocol;
    const $protocol_position = $url.lastIndexOf( $protocol );
    const $domain_extension_position = $url.lastIndexOf( '.' );

    return (
        $protocol_position === 0 &&
        [ 'http:', 'https:' ].indexOf( $protocol ) !== - 1 &&
        $domain_extension_position > 2 && $url.length - $domain_extension_position > 2
    );

};

0

如果您还需要支持,请https://localhost:3000使用此[Devshed] regex的修改版本。

    function isURL(url) {
        if(!url) return false;
        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
            'localhost' + // OR localhost
            '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
            '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
            '(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator
        return pattern.test(url);
    }

0

有一些使用URL构造函数的测试,它们不能描述输入是字符串还是URL对象。

// Testing whether something is a URL
function isURL(url) {
    return toString.call(url) === "[object URL]";
}

// Testing whether the input is both a string and valid url:
function isUrl(url) {
    try {
        return toString.call(url) === "[object String]" && !!(new URL(url));
    } catch (_) {
        return false;  
    }
}

0

2020更新。为了扩展@iamnewton和@Fernando Chavez Herrera的出色回答,我开始看到@URL路径中使用了它。

因此,更新后的正则表达式为:

RegExp('(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+@]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$', 'i');

如果要在查询字符串和哈希中允许它,请使用:

RegExp('(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+@]*)*(\\?[;&a-z\\d%_.~+=-@]*)?(\\#[-a-z\\d_@]*)?$', 'i');

就是说,我不确定@查询字符串或哈希中是否有禁止使用的白皮书规则。


0

已经有很多答案,但是这是另外一个贡献:直接来自URLpolyfill有效性检查,使用带有input元素type="url"来利用浏览器的内置有效性检查:

var inputElement = doc.createElement('input');
inputElement.type = 'url';
inputElement.value = url;

if (!inputElement.checkValidity()) {
    throw new TypeError('Invalid URL');
}

资源

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.