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

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

评论

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

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

正如您可以在URL RFC中阅读的那样,使String成为有效URL的实际上唯一必要的部分是冒号。有效URL如下:

请参阅stackoverflow.com/a/3975573/572180

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

#1 楼

答案相关的问题:

Javascript正则表达式URL匹配

或来自Devshed的此正则表达式:

评论


@Bruno:很有可能它们在内部用单独的标题和URL保存,例如{title:“ Stackoverflow”,uri:“ http://stackoverflow.com”}更新:实际上,请参见code.google.com/chrome/ extensions / bookmarks.html

– Marcel Korpel
2011年4月19日在13:55



尝试使用您的示例。但是我在萤火虫上看到一个错误,说无效的量词。任何想法?

–西西尔
2012年7月10日18:49



函数返回:SyntaxError:无效的正则表达式:/ ^(https?://)?(((([[a-zd]([a-zd-] * [a-zd])*)。)+ [az] { 2,} |((d {1,3}。){3} d {1,3}))(:d +)?(/ [-a-zd%_。〜+] *)*(?[; &a-zd%_。〜+ =-] *)?(#[-a-zd _] *)?$ /:无效的Google Chrome浏览器(版本30.0.1599.101)(Mac OS X:10.8.5)

– dimitru博士
13-10-19在13:37

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

–凯尔
2015年6月5日在16:13

在Firefox 52.0.2中,使用“长”字符串(实际上还不算太久,40个字符就足够了)的测试速度非常慢,以至于我不得不终止脚本。不知道这是Firefox错误还是表达式本身有问题。

–youen
17年4月27日在12:31

#2 楼

如果要检查字符串是否为有效的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),e。 g .:


www.example.com无效的URL(丢失方案)

javascript:void(0)是有效的URL,尽管不是HTTP的
http://..是有效URL,主机为..(是否解析取决于您的DNS)

https://example..com是有效URL,与上面的相同


#4 楼

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

设置hrefanchor属性时,将设置其他各种属性。

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,因此可以执行以下操作:

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


评论


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

–山姆·贝克汉姆
16-2-25在15:37



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

–卢克
16年2月25日在22:48

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

–chrmod
16年5月15日在16:01

函数isValidURL(str):比使用正则表达式好得多!谢谢!

–罗德里戈
18年4月13日在15:55

解决问题的非常简单的方法。这些属性是实验性的:developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement

– Boris D. Teoharov
18年11月30日在14:19

#5 楼

我正在使用以下功能来验证是否带有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 




评论


似乎是一个不错的解决方案!您能否添加一些测试来证明它在某些极端情况下仍然有效(例如,请参见这些注释)?

–巴吉
18年4月16日在7:36



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

– Vikasdeep Singh
18年4月16日在8:00

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

–aaaamarks
18年4月16日在15:58

@aamarks我检查了您的答案。您的答案因https://sdfasdp.ppppppppppppp而失败,即返回true,但我的返回false,这是我想的。

– Vikasdeep Singh
18年4月17日在2:23

它返回sandf@gmail.com的true ...应该吗?我想应该不会
–佐伊卜·阿里(Zohaib Ali)
19年1月2日,9:05

#12 楼

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

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

我见过另外一个存储库,它可能在dperini / regex-weburl.js中对isURL可能更好,但是它非常复杂。它具有更大的有效和无效URL测试列表。上面简单的一个仍然可以传递所有正数,并且只能阻止一些奇怪的负数,例如http://a.b--c.de/以及特殊的ips。使用浏览器的“开发人员工具”检查器对dperini / regex-weburl.js进行测试。

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


,然后测试'a's的字符串。

评论


我检查了你的答案。您的答案因sdfasdp.ppppppppppppp而失败,即返回true但预期为false

– Vikasdeep Singh
18年4月17日在8:26

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

–aaaamarks
18年4月17日在22:12

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

–aaaamarks
18-4-23在20:27



#15 楼

使用validateator.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


评论


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

–贾维德·贾梅(Javid Jamae)
20-3-26在2:07



不错的图书馆。它不仅可以验证URL,还可以验证许多其他内容。

–贝米佩菲
20/12/2在11:09

#16 楼

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



例如域名对连字符的限制很复杂: >一个。中间可以有许多连续的连字符。

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

c。第3个字符和第4个字符不能都是连字符

同样,端口号只能在1-65535范围内。如果您提取端口部分并转换为int,这很容易检查,但是使用正则表达式很难检查。
也没有简单的方法来检查有效的域扩展名。一些国家/地区具有二级域名(例如'co.uk'),或者扩展名可以是一个长词,例如'.international'。并定期添加新的TLD。此类内容只能通过硬编码列表进行检查。 (请参阅https://en.wikipedia.org/wiki/Top-level_domain)
然后有磁铁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] + ']');
} 




#22 楼

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

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


添加锚元素可以触发click()方法。
'GET'对具有挑战性的url进行ajax调用是可以的,但是由于CORS的策略,它具有各种局限性,而不是使用ajax,因为url可能在我服务器的域之外。使用fetch API的解决方法类似于ajax。
另一个问题是我的服务器处于https协议下,并且在调用非安全url时引发异常。可以想到正在获取一些使用javascript尝试执行CURL之类的工具来执行curl -I <url>的工具。不幸的是,我没有找到任何东西,而且不可能。我将不胜感激对此的任何评论。但是,最后,我有一台运行PHP的服务器,并且由于几乎所有请求都使用Ajax,因此我在服务器端编写了一个函数来执行在那里卷曲请求并返回浏览器。

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

评论


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

–aaaamarks
18年4月23日在21:45

#23 楼

这似乎是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”,它也会使其有效,如果地址有效。

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

#25 楼

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

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


#26 楼

Mathias Bynens已编译了带有测试URL的知名URL正则表达式列表。没有什么理由写一个新的正则表达式。只需选择一个最适合您的现有正则表达式即可。 Bynens列表中的所有正则表达式都会产生误报和误报。

我建议您使用现有的URL解析器(例如JavaScript中的new URL('http://www.example.com/')),然后对要执行的检查应用检查URL解析和标准化形式。它的组件。使用JavaScript URL接口还有一个额外的好处,就是它只接受浏览器真正接受的此类URL。例如http://w_w_w.example.com/http://www..example.com/http://123.example.com/都具有无效的主机名部分,但是我知道的每个浏览器都将尝试打开它们而不会引起投诉,并且当您在/etc/hosts/中为这些无效名称指定IP地址时,此类URL甚至可以在您的计算机上使用。 br />
因此,问题不仅仅在于URL是否有效,而在于在特定的上下文中哪些URL可以工作并且应该被允许。

如果您想做URL验证中有许多容易忽略的细节和极端情况:


URL可能包含http://user:password@www.example.com/中的凭据。
端口号必须在0-范围内65535,但是您可能仍要排除通配符端口0。
端口号可能带有前导零,如http://www.example.com:000080/。IPv4地址决不限于0-255范围内的4个十进制整数。您可以使用1-4个整数,它们可以是十进制,八进制或十六进制。 URL 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)等可能会对安全性或隐私产生影响。例如,如果您允许他们作为论坛中用户头像的地址,则会导致用户的浏览器在其本地网络和物联网中发送未经请求的网络请求,此类请求可能会导致有趣而又不太有趣的事情。
出于相同的原因,您可能希望放弃指向不完全合格的主机名的链接,换句话说,就是没有点的主机名。 )。
链接的主机名部分可能包含IPv6地址的尖括号,如http:// [:: 1]。
IPv6地址也具有专用网络或链接本地地址等的范围。
/>如果您阻止某些IPv4地址,请记住,例如https://127.0.0.1和https:// [:: ffff:127.0.0.1]指向同一资源(如果您的计算机的回送设备是URL的主机名部分现在可能包含Unicode,因此字符范围http://www.stackoverflow.com.绝对不再足够。
许多顶级域的注册表都定义了特定的限制,例如在允许的范围内。 Unicode字符集。或它们再细分其名称空间(如[-0-9a-zA-z]和许多其他名称空间)。 >Unicode顶级域(及其带有“ xn--”的punycode编码)仍必须仅包含字母,但谁想在正则表达式中进行检查?

这些限制和规则适用于哪些项目需求和品味。

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



JavaScript / Typescript版本((角)前端)

Perl版本后端

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