Rails: каков хороший способ проверки ссылок (url)?
Мне было интересно, как лучше всего проверить URL-адреса в Rails. Я думал об использовании регулярного выражения, но не уверен, что это лучшая практика.
и, если бы я использовал регулярное выражение, может ли кто-нибудь предложить мне его? Я все еще новичок в регулярном выражении.
20 ответов:
проверка URL-адреса-это сложная работа. Это очень широкий запрос.
что именно вы хотите сделать? Вы хотите проверить формат URL, существование или что? Есть несколько возможностей, в зависимости от того, что вы хотите сделать.
регулярное выражение может проверить формат URL. Но даже сложное регулярное выражение не может гарантировать, что вы имеете дело с допустимым URL.
например, если вы берете простое регулярное выражение, он, вероятно, отклонит следующий хост
http://invalid##host.comно это позволит
http://invalid-host.fooэто допустимый хост, но не допустимый домен, если вы рассматриваете существующие дву. Действительно, решение будет работать, если вы хотите проверить имя хоста, а не домен, потому что следующее является допустимым именем хоста
http://host.fooа также следующий
http://localhostтеперь, позвольте мне дать вам некоторые решения.
если вы хотите проверить домен, а потом вам нужно забыть о регулярных выражениях. Лучшим решением, доступным на данный момент, является публичный список суффиксов, список, поддерживаемый Mozilla. Я создал библиотеку 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-адрес был URL-адресом HTTP/HTTPS, вы можете сделать проверку более точной.
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
Я использую один вкладыш внутри моей модели:
validates :url, :format => URI::regexp(%w(http https))Я думаю, что это достаточно хорошо и просто в использовании. Кроме того, он должен быть теоретически эквивалентен методу Симоны, поскольку он использует то же самое регулярное выражение внутри.
следуя идее Симоны, вы можете легко создать свой собственный валидатор.
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'
в последнее время я столкнулся с той же проблемой (мне нужно было проверить URL-адреса в приложении Rails), но мне пришлось справиться с дополнительным требованием URL-адресов unicode (например,
http://кц.рф)...я исследовал несколько решений и наткнулся на следующее:
- первая и наиболее рекомендуемая вещь-это использование
URI.parse. Проверьте ответ Симоне Карлетти для деталей. Это работает нормально, но не для URL-адресов unicode.- второй метод, который я видел, был один Илья Григорик: http://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails/ в принципе, он пытается сделать запрос на url; если он работает, он действителен...
- третий метод, который я нашел (и тот, который я предпочитаю) - это подход, похожий на
URI.parseно с помощьюaddressableкамень вместоURIstdlib. Этот подход подробно описан здесь:http://rawsyntax.com/blog/url-validation-in-rails-3-and-ruby-in-general/
вот обновленная версия валидатор, опубликованный 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здесь вопрос
addressablegem который охватывает примеры.
Я использую небольшое изменение решение lafeber выше. Он запрещает последовательные точки в имени хоста (например, в
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 вы захотите добавить следующие несколько строк в инициализатор : /config/environments/initializers/active_validators_activation.РБ
# Activate all the validators ActiveValidators.activate(:all)(Примечание : Вы можете заменить все :URL-адрес, или если вы просто хотите проверить конкретные типы значений)
а потом обратно в вашу модель что-то вроде этого
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
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. Вторая часть будет убедиться, что 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и только потом
include UrlValidatorв любой модели, для которой вы хотите проверить url-адрес. Просто в том числе для вариантов.
проверка 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
вы могли бы использовать regex для этого, для меня хорошо работает вот этот:
(^|[\s.:;?\-\]<\(])(ftp|https?:\/\/[-\w;\/?:@&=+$\|\_.!~*\|'()\[\]%#,]+[\w\/#](\(\))?)(?=$|[\s',\|\(\).:;?\-\[\]>\)])
Если вы хотите простую проверку и пользовательское сообщение об ошибке:
validates :some_field_expecting_url_value, format: { with: URI.regexp(%w[http https]), message: 'is not a valid URL' }
мне понравилось monkeypatch модуль URI, чтобы добавить силу? метод
внутри
config/initializers/uri.rbmodule URI def self.valid?(url) uri = URI.parse(url) uri.is_a?(URI::HTTP) && !uri.host.nil? rescue URI::InvalidURIError false end end
Comments