Rails: Was ist eine gute Möglichkeit, Links (URLs) zu validieren?

Ich habe mich gefragt, wie ich die URLs in Rails am besten validieren würde. Ich dachte daran, einen regulären Ausdruck zu verwenden, bin mir aber nicht sicher, ob dies die beste Vorgehensweise ist.

Und wenn ich eine Regex benutzen würde, könnte mir jemand einen vorschlagen? Ich bin noch neu bei Regex.

Das Überprüfen einer URL ist eine knifflige Aufgabe. Es ist auch eine sehr breite Anfrage.

Was willst du genau machen? Möchten Sie das Format der URL, die Existenz oder was? Je nachdem, was Sie tun möchten, gibt es verschiedene Möglichkeiten.

Ein regulärer Ausdruck kann das Format der URL validieren. Aber selbst ein komplexer regulärer Ausdruck kann nicht sicherstellen, dass Sie mit einer gültigen URL arbeiten.

Wenn Sie beispielsweise einen einfachen regulären Ausdruck verwenden, wird der folgende Host wahrscheinlich abgelehnt

http://invalid##host.com 

aber es wird erlauben

 http://invalid-host.foo 

Dies ist ein gültiger Host, aber keine gültige Domain, wenn Sie die vorhandenen TLDs berücksichtigen. In der Tat würde die Lösung funktionieren, wenn Sie den Hostnamen und nicht die Domäne validieren möchten, da der folgende ein gültiger Hostname ist

 http://host.foo 

ebenso der folgende

 http://localhost 

Nun, lassen Sie mich Ihnen einige Lösungen geben.

Wenn Sie eine Domäne validieren möchten, müssen Sie reguläre Ausdrücke vergessen. Die beste derzeit verfügbare Lösung ist die Liste der öffentlichen Suffixe, die von Mozilla verwaltet wird. Ich habe eine Ruby-Bibliothek erstellt, um Domänen anhand der Liste öffentlicher Suffixe zu analysieren und zu validieren. Sie heißt PublicSuffix .

Wenn Sie das Format eines URI / URL validieren möchten, sollten Sie reguläre Ausdrücke verwenden. Anstatt nach einem zu suchen, verwenden Sie die URI.parse Methode Ruby URI.parse .

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

Sie können sogar beschließen, es restriktiver zu machen. Wenn Sie beispielsweise möchten, dass die URL eine HTTP / HTTPS-URL ist, können Sie die Überprüfung genauer vornehmen.

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

Natürlich gibt es eine Menge Verbesserungen, die Sie auf diese Methode anwenden können, einschließlich der Überprüfung eines Pfades oder Schemas.

Zu guter Letzt können Sie diesen Code auch in einen Validator packen:

 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 

Ich benutze einen Liner in meinen Modellen:

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

Ich denke, es ist gut genug und einfach zu bedienen. Außerdem sollte es der Simone-Methode theoretisch gleichwertig sein, da es intern die gleiche Regexp verwendet.

Nach Simones Idee können Sie einfach Ihren eigenen Validator erstellen.

 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 

und dann benutzen

 validates :url, :presence => true, :url => true 

in deinem Modell.

Es gibt auch validure_url gem (das ist nur ein netter Wrapper für Addressable::URI.parse Lösung).

Einfach hinzufügen

 gem 'validate_url' 

zu deinem Gemfile , und dann in Modellen, die du kannst

 validates :click_through_url, url: true 

Diese Frage ist bereits beantwortet, aber was zum Teufel, ich schlage die Lösung vor, die ich verwende.

Die Regexp funktioniert gut mit allen URLs, die ich getroffen habe. Die Setter-Methode soll darauf achten, wenn kein Protokoll erwähnt wird (nehmen wir http: // an).

Und schließlich versuchen wir, die Seite zu holen. Vielleicht sollte ich Weiterleitungen und nicht nur HTTP 200 OK akzeptieren.

 # 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 

und…

 # 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 

Sie können auch valid_url gem versuchen, die URLs ohne das Schema erlaubt, überprüft Domain-Zone und IP-Hostnamen.

Fügen Sie es zu Ihrer Gemdatei hinzu:

gem 'valid_url'

Und dann im Modell:

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

Nur meine 2 Cent:

 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 

EDIT: Changed Regex um Parameter URLs zu entsprechen.

Die Lösung, die für mich funktionierte, war:

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

Ich habe versucht, das Beispiel zu verwenden, das Sie angehängt haben, aber ich unterstütze die URL wie folgt:

Beachten Sie die Verwendung von A und Z, denn wenn Sie ^ und $ verwenden, sehen Sie diese Warnsicherheit von Rails-Validatoren.

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

Ich stieß in letzter Zeit auf das gleiche Problem (ich musste URLs in einer Rails-App validieren), aber ich musste mit der zusätzlichen Anforderung von Unicode-URLs umgehen (zB http://кц.рф ) …

Ich recherchierte ein paar Lösungen und stieß auf folgendes:

  • Die erste und am meisten vorgeschlagene Sache ist die Verwendung von URI.parse . Überprüfen Sie die Antwort von Simone Carletti für Details. Das funktioniert in Ordnung, aber nicht für Unicode-URLs.
  • Die zweite Methode, die ich sah, war die von Ilya Grigorik: http://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails/ Im Grunde versucht er eine Anfrage an die URL; Wenn es funktioniert, ist es gültig …
  • Die dritte Methode, die ich gefunden habe (und die, die ich bevorzuge), ist ein ähnlicher Ansatz wie URI.parse aber mit dem addressable Juwel anstelle der URI stdlib. Dieser Ansatz wird hier detailliert beschrieben: http://rawsyntax.com/blog/url-validation-in-rails-3-and-ruby-in-general/

Hier ist eine aktualisierte Version des Validators von David James . Es wurde von Benjamin Fleischer veröffentlicht . Inzwischen habe ich eine aktualisierte Gabel geschoben, die hier zu finden ist .

 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 

Bitte beachten Sie, dass es immer noch seltsame HTTP-URIs gibt, die als gültige Adressen analysiert werden.

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

Hier ist ein Problem für den addressable Edelstein, der die Beispiele behandelt.

Ich verwende eine kleine Variation der obigen Lafeber-Lösung . Es verbietet aufeinanderfolgende Punkte im Hostnamen (wie zum Beispiel in www.many...dots.com ):

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

URI.parse scheint Schema URI.parse zu verwenden, was in einigen Fällen nicht das ist, was Sie möchten (zB wenn Sie Ihren Benutzern erlauben möchten, URLs in Formularen wie twitter.com/username schnell zu buchstabieren)

Ich habe das ‘activevalidators’-Juwel benutzt und es funktioniert ziemlich gut (nicht nur für die validation von URLs)

Sie können es hier finden

Es ist alles dokumentiert, aber im Grunde, sobald der Edelstein hinzugefügt wurde, werden Sie die folgenden Zeilen in einem Initialisierer hinzufügen wollen: /config/environments/initialisers/active_validators_activation.rb

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

(Hinweis: Sie können Folgendes ersetzen: all: url oder: was auch immer, wenn Sie nur bestimmte Arten von Werten validieren möchten)

Und dann zurück in deinem Modell so etwas

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

Jetzt starte den Server neu und das sollte es sein

Sie können mehrere URLs mit etwas wie:

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

https://github.com/perfectline/validates_url ist ein schönes und einfaches Juwel, das so ziemlich alles für dich erledigen wird

Kürzlich hatte ich das gleiche Problem und ich fand eine Arbeit für gültige URLs.

 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 

Der erste Teil der validate_url-Methode reicht aus, um das URL-Format zu validieren. Der zweite Teil wird sicherstellen, dass die URL existiert, indem eine Anfrage gesendet wird.

Und als Modul

 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 

Und dann include UrlValidator Sie einfach include UrlValidator in jedes Modell ein, für das Sie include UrlValidator validieren möchten. Nur für Optionen.

Die URL-validation kann nicht einfach mit einem regulären Ausdruck gehandhabt werden, da die Anzahl der Websites ständig wächst und neue Domain-Namensschemata immer wieder auftauchen.

In meinem Fall schreibe ich einfach einen benutzerdefinierten Validator, der nach einer erfolgreichen Antwort sucht.

 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 

Ich validiere das record.path meines Modells mit record.path . Ich record.errors[:path] den Fehler auch auf den jeweiligen Attributnamen, indem record.errors[:path] .

Sie können dies einfach durch einen beliebigen Attributnamen ersetzen.

Dann rufe ich einfach den benutzerdefinierten Validator in meinem Modell an.

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

Du könntest Regex dafür benutzen, für mich funktioniert das gut:

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

Wenn Sie eine einfache validation und eine benutzerdefinierte Fehlermeldung wünschen:

  validates :some_field_expecting_url_value, format: { with: URI.regexp(%w[http https]), message: 'is not a valid URL' } 

Ich mochte monkeypatch das URI-Modul, um das gültige hinzuzufügen? Methode

Inside 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