RailsでURLをどのように検証するのが最善か疑問に思いました。正規表現を使用することを考えていましたが、これがベストプラクティスであるかどうかはわかりません。
また、正規表現を使用する場合、誰かが私に正規表現を提案できますか?私はまだ正規表現が初めてです。
URLの検証は難しい作業です。また、非常に広範な要求です。
正確に何をしたいですか? URLの形式、存在、または何を検証しますか?何をしたいかによって、いくつかの可能性があります。
正規表現は、URLの形式を検証できます。しかし、複雑な正規表現であっても、有効なURLを処理していることを保証できません。
たとえば、単純な正規表現を使用する場合、おそらく次のホストを拒否します
http://invalid##Host.com
しかし、それは許可します
http://invalid-Host.foo
これは有効なホストですが、既存のTLDを考慮すると有効なドメインではありません。実際、次のドメインは有効なホスト名であるため、ドメインではなくホスト名を検証する場合、ソリューションは機能します。
http://Host.foo
また、次のもの
http://localhost
それでは、いくつかの解決策をご紹介しましょう。
ドメインを検証する場合は、正規表現を忘れる必要があります。現時点で利用可能な最良のソリューションは、Mozillaが管理しているPublic Suffix Listです。 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
モデル内で1つのライナーを使用します。
validates :url, format: URI::regexp(%w[http https])
使いやすくて十分だと思います。さらに、内部的にはまったく同じ正規表現を使用するため、理論的にはSimoneのメソッドと同等である必要があります。
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
あなたのモデルで。
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
valid_url gemを試すこともできます。これはスキームなしのURLを許可し、ドメインゾーンとIPホスト名をチェックします。
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に一致するように正規表現を変更しました。
私のために働いた解決策は次のとおりでした:
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'
最近同じ問題に遭遇しました(RailsアプリでURLを検証する必要がありました)が、Unicode URLの追加要件(例:http://кц.рф
)に対処しなければなりませんでした...
私はいくつかのソリューションを調査し、次のことに気付きました。
URI.parse
を使用することです。詳細については、Simone Carlettiの回答を確認してください。これは問題なく動作しますが、Unicode URLでは動作しません。URI.parse
に似たアプローチですが、addressable
stdlibの代わりにURI
gemを使用します。このアプローチの詳細は次のとおりです。 http://rawsyntax.com/blog/url-validation-in-Rails-3-and-Ruby-in-general/David Jamesによって投稿された検証者 の更新バージョンです。 ベンジャミン・フライシャー発行 。その間、 here にある更新されたフォークをプッシュしました。
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
addressable
gemの問題 は、例をカバーしています。
上記のラバーソリューション にわずかなバリエーションを使用します。ホスト名に連続するドットを許可しません(たとえば、www.many...dots.com
など)。
%r"\A(https?://)?[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]{2,6}(/.*)?\Z"i
URI.parse
はスキームの接頭辞を義務付けているようです。これは場合によっては望んでいない場合があります(たとえば、ユーザーがTwitter.com/username
などの形式でURLをすばやくスペルできるようにする場合)
私は 'activevalidators' gemを使用しており、それはかなりうまく機能しています(URLの検証だけでなく)
あなたはそれを見つけることができます ここ
それはすべて文書化されていますが、基本的にgemが追加されたら、次の数行をイニシャライザに追加する必要があります:/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
今サーバーを再起動します
次のようなものを使用して、複数のURLを検証できます。
validates_format_of [:field1, :field2], with: URI.regexp(['http', 'https']), allow_nil: true
最近、この同じ問題が発生し、有効な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が存在することを確認します。
https://github.com/perfectline/validates_url は、あなたのためにほとんどすべてを行う素敵でシンプルな宝石です
簡単な検証とカスタムエラーメッセージが必要な場合:
validates :some_field_expecting_url_value,
format: {
with: URI.regexp(%w[http https]),
message: 'is not a valid URL'
}
これには正規表現を使用できますが、私にとってはこれがうまく機能しています:
(^|[\s.:;?\-\]<\(])(ftp|https?:\/\/[-\w;\/?:@&=+$\|\_.!~*\|'()\[\]%#,]+[\w\/#](\(\))?)(?=$|[\s',\|\(\).:;?\-\[\]>\)])
URL検証は、正規表現を使用するだけでは処理できません。Webサイトの数が増え続け、新しいドメイン命名スキームが登場し続けるためです。
私の場合、成功した応答をチェックするカスタムバリデータを作成するだけです。
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
を使用して、モデルのpath
属性を検証しています。また、record.errors[:path]
を使用して、エラーをそれぞれの属性名にプッシュしています。
これを単純に任意の属性名に置き換えることができます。
次に、モデル内のカスタムバリデーターを呼び出します。
class Url < ApplicationRecord
# validations
validates_presence_of :path
validates_with UrlValidator
end
有効を追加するために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
そして、URLを検証したいモデルのinclude UrlValidator
だけです。オプションを含めるだけです。