验证 URL 是一项棘手的工作。这也是一个非常广泛的要求。
你到底想做什么?您想要验证 URL 的格式、是否存在或者什么?有多种可能性,具体取决于您想要做什么。
正则表达式可以验证 URL 的格式。但即使是复杂的正则表达式也无法确保您处理的是有效的 URL。
例如,如果您采用简单的正则表达式,它可能会拒绝以下主机
http://invalid##host.com
但它会允许
http://invalid-host.foo
这是一个有效的主机,但如果您考虑现有的 TLD,则不是一个有效的域。事实上,如果您想验证主机名而不是域,则该解决方案可行,因为以下是有效的主机名
http://host.foo
还有下面这个
http://localhost
现在,让我给你一些解决方案。
如果您想验证域,那么您需要忘记正则表达式。目前可用的最佳解决方案是公共后缀列表,该列表由 Mozilla 维护。我创建了一个 Ruby 库来根据公共后缀列表解析和验证域,它被称为公共后缀.
如果您想验证 URI/URL 的格式,那么您可能需要使用正则表达式。不用搜索,而是使用内置的 RubyURI.parse
method.
require 'uri'
def valid_url?(uri)
uri = URI.parse(uri) && uri.host.present?
rescue URI::InvalidURIError
false
end
您甚至可以决定使其更具限制性。例如,如果您希望 URL 是 HTTP/HTTPS URL,那么您可以使验证更加准确。
require 'uri'
def valid_url?(url)
uri = URI.parse(url)
uri.is_a?(URI::HTTP) && uri.host.present?
rescue URI::InvalidURIError
false
end
当然,您可以对此方法应用大量改进,包括检查路径或方案。
最后但并非最不重要的一点是,您还可以将此代码打包到验证器中:
class HttpUrlValidator < ActiveModel::EachValidator
def self.compliant?(value)
uri = URI.parse(value)
uri.is_a?(URI::HTTP) && uri.host.present?
rescue URI::InvalidURIError
false
end
def validate_each(record, attribute, value)
unless value.present? && self.class.compliant?(value)
record.errors.add(attribute, "is not a valid HTTP URL")
end
end
end
# in the model
validates :example_attribute, http_url: true
新 URI 版本的注意事项(即 0.12.1)
.present?
/ .blank?
将是验证主机的更准确方法,而不是使用uri.host.nil?
要不就if uri.host
以前(即 URI v 0.11)。
URI.parse("https:///394") 的示例:
- 新的 URI 版本(0.12),
host
将返回一个空字符串,并且/394
成为一条路径。 #<:https>
- 旧 URI 版本 (0.11),
host
将返回一个空字符串,并且/394
也成为一条路。 #<:https>