《如何使用 JavaScript 解析 URL?》一文中介绍过 URL 地址的解析方法。而我们的日常开发中,另一个会时常遇到的应用场景应该是如何使用 JavaScript 校验字符串是否为合法的 URL 地址?今天这篇文章应该算是《如何使用 JavaScript 解析 URL?》一文的姊妹篇了,我们来看看 URL 地址合法性的判断思路又是怎么样的。

URL 格式

要校验 URL 地址的合法性,当然我们还是需要(再次)回顾以下一个合法的 URL 地址格式是怎么样的?

完整的 URL 信息包括以下几点:

  • 协议(protocol):采用的协议方案;
  • 登录信息(username & password):(可选项)指定用户名和密码,用来从服务器获取资源时的认证信息;
  • 服务器地址(hostname):待访问的服务器地址。可以是域名形式也可以是 IP 地址;
  • 服务器端口号(port):;指定服务器连接网路的端口号;
  • 带层次的文件路径(pathname):指定服务器上的文件路径来定位特指的资源;
  • 查询字符串(search):(可选项)查询字符串参数;
  • 片段标识符(hash):(可选项)用来标记已获取资源中的子资源;

正则表达式

不像解析 URL 地址,JavaScript 中并没有提供校验 URL 合法性的内置方法。《如何使用 JavaScript 解析 URL?》中介绍的 URL 构造函数,虽然提供了一个静态方法:URL.canParse(),但无奈的是目前这个方法的浏览器支持情况实在太差。

所以目前还只能使用正则表达式的方式来判断字符串是否为合法的 URL 格式。根据前文介绍的 URL 格式的内容,使用 JavaScript 的正则表达式表示大致如下:

/^((protocol):)?\/\/((username):(password)@)?(hostname)(:(port))?(pathname)(\\?(search))?(#(hash))?/

实际的 JavaScript 正则表达式代码实现则是这样的:

const pattern = /^(([^:/?#]+):)?\/\/(([^/?#]+):(.+)@)?([^/?#:]*)(:(\d+))?([^?#]*)(\\?([^#]*))?(#(.*))?/

isURL() 方法实现

虽然前文已经给出了一个完整的 JavaScript 正则表达式,但在实际实现 isURL() 方法的时候,我们并不会直接这么处理。因为上面的正则表达式比较复杂,如果你使用 Sonarlint 之类的工具对前文的正则表达式进行代码扫描,会提示该正则表达式的复杂度超标了,过于复杂。建议将表达式拆分为职责更简单的子表达式。

所以,在 types.js 项目中,将大而全的正则表达式拆分为以下实现:

const _getURLPattern = () => {
  const protocol = '(\\w+:)?'
  const user = '([^\\/\\?\\#\\:]+)'
  const password = '(.+)'
  const auth = '(' + user + ':' + password + '@)?'
  const address = '(([a-z\\d]([a-z\\d-]*[a-z\\d])*(\\.)?)+[a-z]{2,})'
  const ip = '((\\d{1,3}\\.){3}\\d{1,3})'
  const hostname = '(' + address + '|' + ip + ')'
  const port = '(\\:\\d+)?'
  const host = '(' + hostname + port + ')'
  const pathname = '((\\/[-a-z\\d%_.~+]*)*)'
  const search = '(\\?[;&a-z\\d%_.~+=-]*)?'
  const path = '(' + pathname + search + ')*'
  const hash = '(\\#[-a-z\\d_]*)?'
  const url = '^' + protocol + '\\/\\/' + auth + host + path + hash + '$'

  return new RegExp(url, 'i')
}

export default _getURLPattern

这么一调整,每个子表达式的职责就很单一,复杂度就大大降低了。而代码的可读性和可维护性也是明显的有了提升。

Ok,一切准备就绪,完整的 isURL() 方法实现如下:

import isString from './isString'
import _getURLPattern from './_getURLPattern'

/**
 * 判断字符串是否为有效的 URL 地址
 * ========================================================================
 * @method isURL
 * @category Lang
 * @param {String} str - 要检测的字符串
 * @returns {Boolean} 'val' 是有效的 URL 字符串格式,返回 true,否则返回 false
 */
const isURL = (str) => {
  const pattern = _getURLPattern()

  return isString(str) && !!pattern.test(str)
}

export default isURL

结束语

希望通过此文,对于初学 JavaScript 的同学了解如何使用 JavaScript 校验字符串是否为合法的 URL 地址有所帮助。同时也期待 JavaScript 内置的方法能得到所有主流浏览器的支持。当然,如果你有什么更好的解决思路,也欢迎留言评论。


0 条评论

发表回复

Avatar placeholder