回答:
URLの検証は難しい作業です。それはまた、非常に幅広い要求です。
正確に何をしたいですか?URLの形式、存在、または何を検証しますか?何をしたいかに応じて、いくつかの可能性があります。
正規表現は、URLの形式を検証できます。ただし、複雑な正規表現であっても、有効なURLを処理しているとは限りません。
たとえば、単純な正規表現を使用する場合、おそらく次のホストを拒否します
http://invalid##host.com
しかし、それは可能になります
http://invalid-host.foo
これは有効なホストですが、既存のTLDを考慮すると有効なドメインではありません。実際、次のホスト名は有効なホスト名であるため、ドメインではなくホスト名を検証する場合にソリューションが機能します。
http://host.foo
同様に次のもの
http://localhost
では、いくつかの解決策を紹介しましょう。
ドメインを検証する場合は、正規表現を忘れる必要があります。現時点で利用可能な最良のソリューションは、Mozillaが管理するリストであるパブリックサフィックスリストです。Public Suffix Listに対してドメインを解析および検証するRubyライブラリを作成しました。これはPublicSuffixと呼ばれています。
URI / URLの形式を検証する場合は、正規表現を使用することができます。検索する代わりに、組み込みの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にする場合は、検証をより正確にすることができます。
require 'uri'
def valid_url?(url)
uri = URI.parse(url)
uri.is_a?(URI::HTTP) && !uri.host.nil?
rescue URI::InvalidURIError
false
end
もちろん、パスやスキームのチェックなど、この方法に適用できる多くの改善点があります。
最後に重要なことですが、このコードをバリデーターにパッケージ化することもできます:
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
URI::HTTPS
:httpsのURIの(元のためにURI.parse("https://yo.com").class => URI::HTTPS
URI::HTTPS
から継承しているURI:HTTP
、それが私がを使用する理由ですkind_of?
。
URI.parse('http://invalid-host.foo')
そのURIは有効なURLであるため、trueを返します。また、これ.foo
は有効なTLDになりました。iana.org/domains/root/db/foo.html
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
ありvalidate_url宝石(のためだけの素敵なラッパーである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
スキームなしでURLを許可し、ドメインゾーンとip-hostnamesをチェックするvalid_url gemを試すこともできます。
それをGemfileに追加します。
gem 'valid_url'
そしてモデルでは:
class WebSite < ActiveRecord::Base
validates :url, :url => true
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\.-]+)\.([a-z\.]{2,6})([\/\w \.-=\?]*)*\/?$/)
end
編集:パラメータのURLに一致するように正規表現を変更しました。
http://test.com/fdsfsdf?a=b
私のために働いた解決策は:
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では評価がハングします。
最近同じ問題に遭遇しましたが(RailsアプリでURLを検証する必要がありました)、Unicode URLの追加要件(例:)に対処する必要がありましたhttp://кц.рф
...
私はいくつかの解決策を調査し、以下に出くわしました:
URI.parse
。詳細はSimone Carlettiの回答を確認してください。これは問題なく動作しますが、UnicodeのURLでは動作しません。URI.parse
を使用するのと同様の方法です。このアプローチの詳細は次のとおりです。http://rawsyntax.com/blog/url-validation-in-rails-3-and-ruby-in-general/addressable
URI
Addressable::URI.parse('http:///').scheme # => "http"
またはAddressable::URI.parse('Съешь [же] ещё этих мягких французских булок да выпей чаю')
Addressableの観点から完全に大丈夫です:(
これは、David Jamesによって投稿されたバリデーターの更新バージョンです。ベンジャミン・フライシャーから出版されています。その間、私はここにある更新されたフォークをプッシュしました。
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がまだあることに注意してください。
http://google
http://.com
http://ftp://ftp.google.com
http://ssh://google.com
以下は、例をカバーするgemの問題ですaddressable
。
上記のラフェーバーソリューションのわずかなバリエーションを使用します。ホスト名に連続するドットを許可しません(例www.many...dots.com
:など)。
%r"\A(https?://)?[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]{2,6}(/.*)?\Z"i
URI.parse
スキームのプレフィックスを義務付けているようですが、場合によってはこれが適切でない場合があります(たとえば、ユーザーがのような形式のURLをすばやく入力できるようにする場合twitter.com/username
)
私は'activevalidators' gemを使用しており、(url検証だけでなく)非常にうまく機能します
ここで見つけることができます
これはすべてドキュメント化されていますが、基本的にはgemが追加されたら、次の数行をイニシャライザのsayに追加する必要があります。/config/environments/initializers/active_validators_activation.rb
# Activate all the validators
ActiveValidators.activate(:all)
(注:特定のタイプの値を検証するだけの場合は、:allを:urlまたは:whateverに置き換えることができます)
そして、あなたのモデルに戻って、このようなものを
class Url < ActiveRecord::Base
validates :url, :presence => true, :url => true
end
今、サーバーを再起動して、それはそれでなければなりません
https://github.com/perfectline/validates_urlは、ほとんどすべてのことを実行できる、素晴らしくシンプルな宝石です
最近同じ問題が発生し、有効な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メソッドの最初の部分は、URL形式を検証するのに十分です。2番目の部分では、リクエストを送信してURLが存在することを確認します。
有効なURIモジュールを追加するためにURIモジュールをmonkeypatchするのが好きですか?方法
内部 config/initializers/uri.rb
module URI
def self.valid?(url)
uri = URI.parse(url)
uri.is_a?(URI::HTTP) && !uri.host.nil?
rescue URI::InvalidURIError
false
end
end
そしてモジュールとして
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
そして、include UrlValidator
あなたがURLを検証したい任意のモデルで。オプションのためだけを含みます。
Webサイトの数が増え続け、新しいドメインの名前付けスキームが増え続けるため、URL検証は正規表現を使用するだけでは処理できません。
私の場合、成功した応答をチェックするカスタムバリデーターを作成するだけです。
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
をpath
使用してモデルの属性を検証していますrecord.path
。また、を使用して、エラーをそれぞれの属性名にプッシュしていますrecord.errors[:path]
。
これを任意の属性名に置き換えるだけです。
次に、モデルでカスタムバリデーターを呼び出すだけです。
class Url < ApplicationRecord
# validations
validates_presence_of :path
validates_with UrlValidator
end
あなたはこれに正規表現を使うことができます、私にとってこれはうまくいきます:
(^|[\s.:;?\-\]<\(])(ftp|https?:\/\/[-\w;\/?:@&=+$\|\_.!~*\|'()\[\]%#,]+[\w\/#](\(\))?)(?=$|[\s',\|\(\).:;?\-\[\]>\)])