Rails: jaki jest dobry sposób na walidację linków (adresów URL)?
Zastanawiałem się, jak najlepiej zweryfikować adresy URL w Rails. Myślałem o użyciu wyrażenia regularnego, ale nie jestem pewien, czy jest to najlepsza praktyka.
A gdybym miał użyć regex, czy ktoś mógłby mi go zasugerować? Wciąż jestem nowy w Regex.
15 answers
Walidacja adresu URL jest trudnym zadaniem. To również bardzo szeroka Prośba.
Co dokładnie chcesz robić? Czy chcesz zweryfikować format adresu URL, istnienie lub co? Istnieje kilka możliwości, w zależności od tego, co chcesz zrobić.
Wyrażenie regularne może zweryfikować format adresu URL. Ale nawet złożone wyrażenie regularne nie może zapewnić, że masz do czynienia z prawidłowym adresem URL.
Na przykład, jeśli weźmiesz proste wyrażenie regularne, prawdopodobnie odrzuci ono następujący host
http://invalid##host.com
Ale pozwoli
http://invalid-host.foo
Jest to prawidłowy host, ale nie prawidłowa domena, jeśli wziąć pod uwagę istniejące TLD. Rzeczywiście, rozwiązanie będzie działać, jeśli chcesz zweryfikować nazwę hosta, a nie domenę, ponieważ następująca jest poprawna nazwa hosta
http://host.foo
Jak również następujące
http://localhost
Pozwól, że przedstawię Ci kilka rozwiązań.
Jeśli chcesz zweryfikować domenę, musisz zapomnieć o wyrażeniach regularnych. Najlepsze dostępne rozwiązanie obecnie jest to Public sufiks List, Lista utrzymywana przez Mozillę. Stworzyłem bibliotekę Ruby do analizy i walidacji domen względem publicznej listy sufiksów i nazywa się PublicSuffix.
Jeśli chcesz zweryfikować format URI / URL, możesz użyć wyrażeń regularnych. Zamiast szukać, użyj wbudowanej metody Ruby URI.parse
.
require 'uri'
def valid_url?(uri)
uri = URI.parse(uri) && uri.host
rescue URI::InvalidURIError
false
end
Możesz nawet zdecydować się na bardziej restrykcyjne. Na przykład, jeśli chcesz, aby adres URL był adresem URL HTTP/HTTPS, następnie możesz zwiększyć dokładność walidacji.
require 'uri'
def valid_url?(url)
uri = URI.parse(url)
uri.is_a?(URI::HTTP) && !uri.host.nil?
rescue URI::InvalidURIError
false
end
Oczywiście istnieje mnóstwo ulepszeń, które możesz zastosować do tej metody, w tym sprawdzanie ścieżki lub schematu.
Last but not least, you can also packaging this code into a validator:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-28 19:59:48
Używam jednej wkładki wewnątrz moich modeli:
validates :url, format: URI::regexp(%w[http https])
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-25 15:05:44
Podążając za pomysłem Simone, możesz łatwo stworzyć własny walidator.
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
A następnie użyj
validates :url, :presence => true, :url => true
W twoim modelu.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-25 11:43:12
Istnieje również validate_url gem (który jest po prostu ładnym opakowaniem dla Addressable::URI.parse
rozwiązanie).
Po prostu dodaj
gem 'validate_url'
Do twojego Gemfile
, a następnie w modelach możesz
validates :click_through_url, url: true
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-29 13:46:59
To pytanie jest już odpowiedział, Ale co mi tam, proponuję rozwiązanie, którego używam.
Wyrażenie regularne działa dobrze ze wszystkimi adresami URL, które spotkałem. Metoda setter ma dbać o to, czy żaden protokół nie jest wymieniony (Załóżmy http://).
I na koniec próbujemy pobrać stronę. Może powinienem zaakceptować przekierowania i nie tylko 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
I...
# 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-05 14:18:58
Rozwiązanie, które zadziałało dla mnie było:
validates_format_of :url, :with => /\A(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w\.-]*)*\/?\Z/i
Próbowałem użyć jakiegoś przykładu, który załączyłeś, ale wspieram url w ten sposób:
Zwróć uwagę na użycie a i z, ponieważ jeśli użyjesz ^ i $ zobaczysz to Ostrzeżenie z walidatorów 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'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-26 12:42:21
Możesz również spróbować valid_url gem, który pozwala adresom URL bez schematu, sprawdza strefę domeny i nazwy ip-hostów.
Dodaj go do swojego Gemfile:
gem 'valid_url'
A następnie w modelu:
class WebSite < ActiveRecord::Base
validates :url, :url => true
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-18 23:07:13
Tylko moje 2 grosze:
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
EDIT: zmieniono wyrażenia regularne, aby dopasować adresy URL parametrów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-03 08:36:22
Ostatnio napotkałem ten sam problem (musiałem zweryfikować adresy URL w aplikacji Rails), ale musiałem poradzić sobie z dodatkowym wymogiem Unicode (np. http://кц.рф
)...
Zbadałem kilka rozwiązań i natknąłem się na następujące:
- pierwszą i najbardziej sugerowaną rzeczą jest użycie
URI.parse
. Szczegóły znajdziesz w odpowiedzi Simone Carletti. To działa ok, ale nie dla adresów URL unicode. - drugą metodą, jaką widziałem, była ta Ilya Grigorik: http://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails / W zasadzie próbuje wysłać żądanie do adresu url; jeśli to działa, jest ważne...
- trzecia metoda, którą znalazłem (i ta, którą preferuję), to podejście podobne do
URI.parse
, ale za pomocąaddressable
gem zamiastURI
stdlib. To podejście jest szczegółowe tutaj: http://rawsyntax.com/blog/url-validation-in-rails-3-and-ruby-in-general /
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-05 07:56:15
Oto zaktualizowana wersja walidatora opublikowanego przez Davida Jamesa . Został on opublikowany przez Benjamina Fleischera . W międzyczasie wcisnąłem zaktualizowany fork, który można znaleźć tutaj.
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
Proszę zauważyć, że nadal istnieją dziwne Uri HTTP, które są przetwarzane jako poprawne adresy.
http://google
http://.com
http://ftp://ftp.google.com
http://ssh://google.com
Oto problem dla addressable
gem , który obejmuje przykłady.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:03
Używam lekkiego wariantu na lafeber rozwiązanie powyżej.
Wyłącza kolejne kropki w nazwie hosta (np. w www.many...dots.com
):
%r"\A(https?://)?[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]{2,6}(/.*)?\Z"i
URI.parse
wydaje się, że nakazuje prefiks scheme, który w niektórych przypadkach nie jest tym, czego możesz chcieć (np. jeśli chcesz, aby użytkownicy mogli szybko przeliterować adresy URL w formularzach, takich jak twitter.com/username
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:44
Używałem kleju 'activevalidators' i działa całkiem dobrze (nie tylko do walidacji adresów URL)
Możesz go znaleźć tutaj
To wszystko jest udokumentowane, ale zasadniczo po dodaniu gem będziesz chciał dodać kilka następujących linii w inicjalizatorze powiedzieć: / config/environments/initializers / active_validators_activation.rb
# Activate all the validators
ActiveValidators.activate(:all)
(Uwaga: możesz zastąpić :all przez: url lub: whatever, jeśli chcesz tylko zweryfikować określone typy wartości)
Oraz potem w twoim modelu coś takiego
class Url < ActiveRecord::Base
validates :url, :presence => true, :url => true
end
Teraz Uruchom ponownie Serwer i to powinno być to
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-07 15:04:24
Jeśli chcesz prostej walidacji i niestandardowego komunikatu o błędzie:
validates :some_field_expecting_url_value,
format: {
with: URI.regexp(%w[http https]),
message: 'is not a valid URL'
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-17 23:13:44
Możesz zweryfikować wiele adresów URL używając czegoś takiego jak:
validates_format_of [:field1, :field2], with: URI.regexp(['http', 'https']), allow_nil: true
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-28 16:17:46
Https://github.com/perfectline/validates_url to miły i prosty klejnot, który zrobi dla ciebie prawie wszystko