Rails:什么是validation链接(URL)的好方法?

我想知道如何在Rails中最好地validationURL。 我正在考虑使用正则expression式,但不确定这是否是最佳做法。

而且,如果我使用正则expression式,有人可以给我一个build议吗? 我对Regex还是个新手。

validationURL是一件棘手的工作。 这也是一个非常广泛的要求。

你想干什么? 你想validation的URL的格式,存在,还是什么? 有几种可能性,取决于你想要做什么。

正则expression式可以validationURL的格式。 但即使是一个复杂的正则expression式也不能确保你正在处理一个有效的URL。

例如,如果你采取一个简单的正则expression式,它可能会拒绝下面的主机

http://invalid##host.com 

但它会允许的

 http://invalid-host.foo 

这是一个有效的主机,但如果您考虑现有的顶级域名(TLD),则不是有效的域名。 事实上,如果您想validation主机名,而不是域,因为下面的一个是有效的主机名,解决scheme将工作

 http://host.foo 

以及下面的一个

 http://localhost 

现在,让我给你一些解决scheme。

如果你想validation一个域,那么你需要忘记正则expression式。 目前可用的最佳解决scheme是由Mozilla维护的公共后缀列表。 我创build了一个Ruby库来parsing和validation公共后缀列表中的域,它被称为PublicSuffix 。

如果你想validation一个URI / URL的格式,那么你可能想要使用正则expression式。 而不是search一个,使用内置的Ruby URI.parse方法。

 require 'uri' def valid_url?(uri) uri = URI.parse(uri) && !uri.host.nil? rescue URI::InvalidURIError false end 

你甚至可以决定使其更加严格。 例如,如果您希望URL是HTTP / HTTPS URL,那么您可以使validation更加准确。

 require 'uri' def valid_url?(url) uri = URI.parse(url) uri.is_a?(URI::HTTP) && !uri.host.nil? rescue URI::InvalidURIError false end 

当然,你可以对这个方法有很多的改进,包括检查path或scheme。

最后但并非最不重要的,你也可以把这个代码打包到一个validation器中:

 class HttpUrlValidator < ActiveModel::EachValidator def self.compliant?(value) uri = URI.parse(value) uri.is_a?(URI::HTTP) && !uri.host.nil? 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 

我在模特里面使用了一个衬垫:

validates :url, :format => URI::regexp(%w(http https))

我觉得很好用,使用简单。 此外,它在理论上应该与Simone的方法相同,因为它在内部使用相同的正则expression式。

遵循Simone的想法,您可以轻松创build自己的validation器。

 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 

在你的模型中。

还有validate_url gem (这只是Addressable::URI.parse解决scheme的一个很好的包装)。

只需添加

 gem 'validate_url' 

到你的Gemfile ,然后在模型中,你可以

 validates :click_through_url, url: true 

这个问题已经得到解答,但是我提出了我正在使用的解决scheme。

该正则expression式正常工作与我见过的所有url。 如果没有提到协议(假设http://),那么setter方法就要小心。

最后,我们尝试获取页面。 也许我应该接受redirect,而不仅仅是HTTP 200 OK。

 # app/models/my_model.rb validates :website, :allow_blank => true, :uri => { :format => /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[az]{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 

只是我2美分:

 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\.-]+)\.([az\.]{2,6})([\/\w \.-=\?]*)*\/?$/) end 

编辑:改变正则expression式匹配参数url。

您也可以尝试valid_url gem,它允许没有该scheme的URL,检查域名区域和ip-hostnames。

将它添加到你的Gemfile:

gem 'valid_url'

然后在模型中:

 class WebSite < ActiveRecord::Base validates :url, :url => true end 

我最近遇到了同样的问题(我需要validationRails应用程序中的url),但是我必须应付unicode url的额外要求(例如http://кц.рф )。

我研究了几个解决scheme,并碰到以下内容:

  • 第一个和build议最多的是使用URI.parse 。 详细信息请查看Simone Carletti的答案。 这工作正常,但不适用于unicodeurl。
  • 我看到的第二种方法是Ilya Grigorik的方法: http ://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails/基本上,他试图向url; 如果它的工作,这是有效的… … –
  • 我发现的第三种方法(和我喜欢的方法)是类似于URI.parse的方法,但是使用addressable gem而不是URI stdlib。 这种方法详细在这里: http : //rawsyntax.com/blog/url-validation-in-rails-3-and-ruby-in-general/

为我工作的解决scheme是:

 validates_format_of :url, :with => /\A(https?:\/\/)?([\da-z\.-]+)\.([az\.]{2,6})([\/\w\.-]*)*\/?\Z/i 

我曾尝试使用您附加的一些示例,但我支持如下的url:

注意使用A和Z,因为如果你使用^和$,你会看到来自Railsvalidation器的警告安全。

  Valid ones: 'www.crowdint.com' 'crowdint.com' 'http://crowdint.com' 'http://www.crowdint.com' Invalid ones: 'http://www.crowdint. com' 'http://fake' 'http:fake' 

这是David James发布的validation器的更新版本。 它由Benjamin Fleischer出版 。 同时,我推出了一个更新的叉子,可以在这里find。

 require '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 URI被parsing为有效地址。

 http://google http://.com http://ftp://ftp.google.com http://ssh://google.com 

这是一个涉及范例的addressable gem的问题 。

我在上面的lafeber解决scheme上略有变化。 它不允许主机名中的连续点(例如www.many...dots.com ):

 %r"\A(https?://)?[az\d\-]+(\.[az\d\-]+)*\.[az]{2,6}(/.*)?\Z"i 

URI.parse似乎要求使用scheme前缀,这在某些情况下不是您可能想要的(例如,如果您希望允许用户以twitter.com/username等forms快速拼写URL)

我一直在使用“activevalidators”的gem ,它的工作很好(不仅仅是为了validationurl)

你可以在这里find它

这是所有logging,但基本上一旦gem添加,你会想在初始化程序中添加以下几行说:/ config / environments / initializers / active_validators_activation.rb

 # Activate all the validators ActiveValidators.activate(:all) 

(注意:你可以用:url或者:全部replace:如果你只想validation特定types的值)

然后回到你的模型中

 class Url < ActiveRecord::Base validates :url, :presence => true, :url => true end 

现在重新启动服务器 ,应该是这样的

您可以使用类似以下的方式validation多个url:

 validates_format_of [:field1, :field2], with: URI.regexp(['http', 'https']), allow_nil: true 

最近我有这个相同的问题,我find了有效的url的工作。

 validates_format_of :url, :with => URI::regexp(%w(http https)) validate :validate_url def validate_url unless self.url.blank? begin source = URI.parse(self.url) resp = Net::HTTP.get_response(source) rescue URI::InvalidURIError errors.add(:url,'is Invalid') rescue SocketError errors.add(:url,'is Invalid') end end 

validate_url方法的第一部分足以validationurl格式。 第二部分将通过发送请求确保url存在。

并作为一个模块

 module UrlValidator extend ActiveSupport::Concern included do validates :url, presence: true, uniqueness: true validate :url_format end def url_format begin errors.add(:url, "Invalid url") unless URI(self.url).is_a?(URI::HTTP) rescue URI::InvalidURIError errors.add(:url, "Invalid url") end end end 

然后在任何你想validationurl的模型中include UrlValidator 。 只包括选项。

随着网站数量不断增长以及新的域名命名scheme不断出现,URLvalidation无法简单地通过使用正则expression式来处理。

在我的情况下,我只是写一个自定义validation器,检查一个成功的响应。

 class UrlValidator < ActiveModel::Validator def validate(record) begin url = URI.parse(record.path) response = Net::HTTP.get(url) true if response.is_a?(Net::HTTPSuccess) rescue StandardError => error record.errors[:path] << 'Web address is invalid' false end end end 

我正在使用record.pathvalidation我的模型的path属性。 我也通过使用record.errors[:path]将错误推送到相应的属性名称。

您可以简单地用任何属性名称replace它。

然后,我只是在我的模型中调用自定义validation器。

 class Url < ApplicationRecord # validations validates_presence_of :path validates_with UrlValidator end 

你可以使用这个正则expression式,对我来说这个工作很好:

 (^|[\s.:;?\-\]<\(])(ftp|https?:\/\/[-\w;\/?:@&=+$\|\_.!~*\|'()\[\]%#,]+[\w\/#](\(\))?)(?=$|[\s',\|\(\).:;?\-\[\]>\)])