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.path
validation我的模型的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',\|\(\).:;?\-\[\]>\)])