我想知道在Rails中如何最好地验证URL。 我考虑使用正则表达式,但不确定这是否是最佳实践。
如果我要使用正则表达式,有人可以向我建议一个吗? 我对正则表达式还很陌生。
我想知道在Rails中如何最好地验证URL。 我考虑使用正则表达式,但不确定这是否是最佳实践。
如果我要使用正则表达式,有人可以向我建议一个吗? 我对正则表达式还很陌生。
验证URL是一项棘手的工作。这也是一个非常广泛的请求。
你想要做什么,确切地说?你想要验证URL的格式、存在性还是其他什么?根据你想要做什么,有几种可能性。
正则表达式可以验证URL的格式。但即使是复杂的正则表达式也不能保证你正在处理一个有效的URL。
例如,如果你使用一个简单的正则表达式,它可能会拒绝以下主机:
http://invalid##host.com
但它将允许
http://invalid-host.foo
这是一个有效的主机,但如果考虑到现有的顶级域名,则不是有效的域名。事实上,如果您想要验证主机名而不是域名,那么以下解决方案将起作用,因为以下内容是有效的主机名。
http://host.foo
还有下面这个
http://localhost
现在,让我给你一些解决方案。
如果你想验证一个域名,那么你需要忘记正则表达式。目前最好的解决方案是Public Suffix List(由Mozilla维护的列表)。我创建了一个Ruby库来解析和验证域名是否在Public Suffix List中,并且它叫做PublicSuffix。
如果你想验证URI/URL的格式,那么你可能需要使用正则表达式。不要搜索一个,而是使用内置的Ruby URI.parse
方法。
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
.present?
/ .blank?
是验证主机更准确的方式,而不是使用uri.host.nil?
或之前的if uri.host
(即URI v 0.11)。
例如,对于URI.parse("https:///394"):
host
将返回一个空字符串,/394
成为路径。#<URI::HTTPS https:///394>host
将返回一个空字符串,/394
也成为路径。#<URI::HTTPS https:/394>URI::HTTPS
继承自 URI::HTTP
,这就是我使用 kind_of?
的原因。 - Simone CarlettiURI.parse('http://invalid-host.foo')
返回 true,因为该 URI 是一个有效的 URL。另外请注意,.foo
现在是一个有效的顶级域名。http://www.iana.org/domains/root/db/foo.html - Simone Carlettiwww.google
是一个有效的域名,尤其是现在 .GOOGLE
是一个有效的顶级域:https://github.com/whois/ianawhois/blob/master/GOOGLE。如果您想要验证器明确验证特定的顶级域名,则必须添加任何您认为适当的业务逻辑。 - Simone Carletti'http://'
与上述模式匹配。请参见:URI::regexp(%w(http https))=~'http:// '
。 - David J.http:fake
这样的网址也是有效的。 - nathanvda按照 Simone 的想法,你可以轻松地创建自己的验证器。
class UrlValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if value.blank?
begin
uri = URI.parse(value)
resp = uri.kind_of?(URI::HTTP)
rescue URI::InvalidURIError
resp = false
end
unless resp == true
record.errors[attribute] << (options[:message] || "is not an url")
end
end
end
并且之后使用 validates :url, :presence => true, :url => true
在你的模型中。URI("http:").kind_of?(URI::HTTP) #=> true
- smathy还有一个validate_url gem(只是对Addressable::URI.parse
方法的良好封装)。
只需添加
gem 'validate_url'
在你的Gemfile
中添加,然后你就可以在模型中使用了
validates :click_through_url, url: true
这个问题已经有答案了,但是无论如何,我提出我正在使用的解决方案。
正则表达式对我遇到的所有url都有效。 setter方法是为了处理没有提及协议的情况(假设为http://)。
最后,我们尝试获取页面。也许我应该接受重定向而不仅仅是HTTP 200 OK。
# app/models/my_model.rb
validates :website, :allow_blank => true, :uri => { :format => /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)/ix }
def website= url_str
unless url_str.blank?
unless url_str.split(':')[0] == 'http' || url_str.split(':')[0] == 'https'
url_str = "http://" + url_str
end
end
write_attribute :website, url_str
end
和...
# app/validators/uri_vaidator.rb
require 'net/http'
# Thanks Ilya! http://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails/
# Original credits: http://blog.inquirylabs.com/2006/04/13/simple-uri-validation/
# HTTP Codes: http://www.ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html
class UriValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
raise(ArgumentError, "A regular expression must be supplied as the :format option of the options hash") unless options[:format].nil? or options[:format].is_a?(Regexp)
configuration = { :message => I18n.t('errors.events.invalid_url'), :format => URI::regexp(%w(http https)) }
configuration.update(options)
if value =~ configuration[:format]
begin # check header response
case Net::HTTP.get_response(URI.parse(value))
when Net::HTTPSuccess then true
else object.errors.add(attribute, configuration[:message]) and false
end
rescue # Recover on DNS failures..
object.errors.add(attribute, configuration[:message]) and false
end
else
object.errors.add(attribute, configuration[:message]) and false
end
end
end
对我有用的解决方案是:
validates_format_of :url, :with => /\A(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w\.-]*)*\/?\Z/i
我尝试使用您附加的示例,但我支持的url格式为:
请注意使用A和Z,因为如果您使用^和$,则会看到Rails验证器的安全警告。
Valid ones:
'www.crowdint.com'
'crowdint.com'
'http://crowdint.com'
'http://www.crowdint.com'
Invalid ones:
'http://www.crowdint. com'
'http://fake'
'http:fake'
"https://portal.example.com/portal/#"
。在Ruby 2.1.6中,评估会挂起。 - Old Pro您还可以尝试valid_url宝石,它允许没有方案的URL,检查域区和IP主机名。
将其添加到Gemfile中:
gem 'valid_url'
然后在模型中:
class WebSite < ActiveRecord::Base
validates :url, :url => true
end
只是我的个人意见:
before_validation :format_website
validate :website_validator
private
def format_website
self.website = "http://#{self.website}" unless self.website[/^https?/]
end
def website_validator
errors[:website] << I18n.t("activerecord.errors.messages.invalid") unless website_valid?
end
def website_valid?
!!website.match(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-=\?]*)*\/?$/)
end
编辑:更改正则表达式以匹配参数URL。
http://test.com/fdsfsdf?a=b
。 - mikdiet最近我遇到了同样的问题(我需要在Rails应用程序中验证url),但我必须应对使用unicode url的额外要求(例如http://кц.рф
)...
我研究了几种解决方案,发现以下方法:
URI.parse
。有关详细信息,请参见Simone Carletti的答案。这个方法可以正常工作,但不能用于unicode url。URI.parse
的方法,但使用addressable
gem而不是URI
stdlib。 该方法在此处详细说明:http://rawsyntax.com/blog/url-validation-in-rails-3-and-ruby-in-general/Addressable :: URI.parse('http:///').scheme#=>“http”
或Addressable :: URI.parse('Съешь[же]ещёэтихмягкихфранцузскихбулокдавыпейчаю')
都是完全可以接受的 :( - smileartrequire 'addressable/uri'
# Source: http://gist.github.com/bf4/5320847
# Accepts options[:message] and options[:allowed_protocols]
# spec/validators/uri_validator_spec.rb
class UriValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
uri = parse_uri(value)
if !uri
record.errors[attribute] << generic_failure_message
elsif !allowed_protocols.include?(uri.scheme)
record.errors[attribute] << "must begin with #{allowed_protocols_humanized}"
end
end
private
def generic_failure_message
options[:message] || "is an invalid URL"
end
def allowed_protocols_humanized
allowed_protocols.to_sentence(:two_words_connector => ' or ')
end
def allowed_protocols
@allowed_protocols ||= [(options[:allowed_protocols] || ['http', 'https'])].flatten
end
def parse_uri(value)
uri = Addressable::URI.parse(value)
uri.scheme && uri.host && uri
rescue URI::InvalidURIError, Addressable::URI::InvalidURIError, TypeError
end
end
...
require 'spec_helper'
# Source: http://gist.github.com/bf4/5320847
# spec/validators/uri_validator_spec.rb
describe UriValidator do
subject do
Class.new do
include ActiveModel::Validations
attr_accessor :url
validates :url, uri: true
end.new
end
it "should be valid for a valid http url" do
subject.url = 'http://www.google.com'
subject.valid?
subject.errors.full_messages.should == []
end
['http://google', 'http://.com', 'http://ftp://ftp.google.com', 'http://ssh://google.com'].each do |invalid_url|
it "#{invalid_url.inspect} is a invalid http url" do
subject.url = invalid_url
subject.valid?
subject.errors.full_messages.should == []
end
end
['http:/www.google.com','<>hi'].each do |invalid_url|
it "#{invalid_url.inspect} is an invalid url" do
subject.url = invalid_url
subject.valid?
subject.errors.should have_key(:url)
subject.errors[:url].should include("is an invalid URL")
end
end
['www.google.com','google.com'].each do |invalid_url|
it "#{invalid_url.inspect} is an invalid url" do
subject.url = invalid_url
subject.valid?
subject.errors.should have_key(:url)
subject.errors[:url].should include("is an invalid URL")
end
end
['ftp://ftp.google.com','ssh://google.com'].each do |invalid_url|
it "#{invalid_url.inspect} is an invalid url" do
subject.url = invalid_url
subject.valid?
subject.errors.should have_key(:url)
subject.errors[:url].should include("must begin with http or https")
end
end
end
http://google
http://.com
http://ftp://ftp.google.com
http://ssh://google.com
addressable
gem 相关的 问题,其中包含了一些例子。