ruby on rails - Rieles: ¿Cuál es una buena forma de validar enlaces(URL)?
ruby-on-rails regex (18)
Me preguntaba cómo mejor validaría las URL en Rails. Estaba pensando en usar una expresión regular, pero no estoy seguro de si esta es la mejor práctica.
Y, si tuviera que usar una expresión regular, ¿alguien podría sugerirme una? Todavía soy nuevo en Regex.
Aquí hay una versión actualizada del validador publicado por David James . Ha sido publicado por Benjamin Fleischer . Mientras tanto, empujé una horquilla actualizada que se puede encontrar 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
Tenga en cuenta que todavía hay URI HTTP extraños que se analizan como direcciones válidas.
http://google
http://.com
http://ftp://ftp.google.com
http://ssh://google.com
Aquí hay un problema para la gema addressable
que cubre los ejemplos.
Esta pregunta ya está respondida, pero qué diablos, propongo la solución que estoy usando.
La expresión regular funciona bien con todas las direcciones URL que he conocido. El método setter es cuidar si no se menciona ningún protocolo (supongamos http: //).
Y finalmente, intentamos buscar la página. Tal vez debería aceptar redirecciones y no solo 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
y...
# 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
He estado usando la gema "validadores activos" y funciona bastante bien (no solo para la validación de URL)
puedes encontrarlo here
Está todo documentado, pero básicamente una vez que la gema se agrega, querrás agregar las siguientes líneas en un inicializador, por ejemplo: /config/environments/initializers/active_validators_activation.rb
# Activate all the validators
ActiveValidators.activate(:all)
(Nota: puede reemplazar: all by: url o: lo que sea si solo quiere validar tipos específicos de valores)
Y luego regresa a tu modelo algo como esto
class Url < ActiveRecord::Base
validates :url, :presence => true, :url => true
end
Ahora reinicie el servidor y eso debería ser
La solución que funcionó para mí fue:
validates_format_of :url, :with => //A(https?:////)?([/da-z/.-]+)/.([a-z/.]{2,6})([///w/.-]*)*//?/Z/i
Intenté usar algunos de los ejemplos que adjuntó, pero estoy soportando la URL de esta manera:
Observe el uso de A y Z porque si usa ^ y $ verá esta advertencia de seguridad de los validadores de 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''
La validación de URL no se puede manejar simplemente mediante el uso de una expresión regular, ya que la cantidad de sitios web sigue creciendo y siguen surgiendo nuevos esquemas de nombres de dominio.
En mi caso, simplemente escribo un validador personalizado que busca una respuesta exitosa.
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
Estoy validando el atributo de path
de mi modelo usando record.path
. También estoy empujando el error al nombre de atributo respectivo usando record.errors[:path]
.
Simplemente puede reemplazar esto con cualquier nombre de atributo.
Luego, simplemente llamo al validador personalizado en mi modelo.
class Url < ApplicationRecord
# validations
validates_presence_of :path
validates_with UrlValidator
end
Me encontré con el mismo problema últimamente (necesitaba validar URL en una aplicación Rails) pero tuve que lidiar con el requisito adicional de las URL unicode (por ejemplo, http://кц.рф
) ...
Investigué un par de soluciones y encontré lo siguiente:
- Lo primero y más sugerido es usar
URI.parse
. Consulte la respuesta de Simone Carletti para más detalles. Esto funciona bien, pero no para urls unicode. - El segundo método que vi fue el de Ilya Grigorik: http://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails/ Básicamente, intenta hacer una solicitud al url; si funciona, es válido ...
- El tercer método que encontré (y el que prefiero) es un enfoque similar a
URI.parse
pero usando la gemaaddressable
lugar de laURI
stdlib. Este enfoque se detalla aquí: http://rawsyntax.com/blog/url-validation-in-rails-3-and-ruby-in-general/
Puede validar varias URL usando algo como:
validates_format_of [:field1, :field2], with: URI.regexp([''http'', ''https'']), allow_nil: true
Puedes usar regex para esto, para mí funciona bien este:
(^|[/s.:;?/-/]</(])(ftp|https?:////[-/w;//?:@&=+$/|/_.!~*/|''()/[/]%#,]+[/w//#](/(/))?)(?=$|[/s'',/|/(/).:;?/-/[/]>/)])
Recientemente tuve este mismo problema y encontré una solución alternativa para las URL válidas.
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
La primera parte del método validate_url es suficiente para validar el formato url. La segunda parte se asegurará de que exista la url mediante el envío de una solicitud.
Siguiendo la idea de Simone, puede crear fácilmente su propio validador.
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
y luego usa
validates :url, :presence => true, :url => true
en tu modelo.
Solo mis 2 centavos:
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
EDITAR: cambio de expresiones regulares para que coincida con las URL de los parámetros.
También hay validate_url gem (que es solo un buen contenedor para la solución de Addressable::URI.parse
).
Solo agrega
gem ''validate_url''
a tu Gemfile
, y luego en los modelos que puedas
validates :click_through_url, url: true
También puede probar la valid_url gem que permite URLs sin el esquema, comprueba la zona de dominio y los nombres de host de IP.
Agrégalo a tu Gemfile:
gem ''valid_url''
Y luego en el modelo:
class WebSite < ActiveRecord::Base
validates :url, :url => true
end
Uso un trazador de líneas dentro de mis modelos:
validates :url, :format => URI::regexp(%w(http https))
Creo que es lo suficientemente bueno y simple de usar. Además, debería ser teóricamente equivalente al método de Simone, ya que utiliza la misma expresión regular internamente.
Utilizo una ligera variación en la solución de lafeber anterior . No permite puntos consecutivos en el nombre de host (como por ejemplo en www.many...dots.com
):
%r"/A(https?://)?[a-z/d/-]+(/.[a-z/d/-]+)*/.[a-z]{2,6}(/.*)?/Z"i
URI.parse
parece URI.parse
de esquema, que en algunos casos no es lo que puede desear (por ejemplo, si desea permitir a los usuarios escribir rápidamente las URL en formularios como twitter.com/username
)
Validar una URL es un trabajo complicado. También es una solicitud muy amplia.
¿Qué quieres hacer, exactamente? ¿Desea validar el formato de la URL, la existencia o qué? Hay varias posibilidades, dependiendo de lo que quieras hacer.
Una expresión regular puede validar el formato de la URL. Pero incluso una expresión regular compleja no puede garantizar que se trata de una URL válida.
Por ejemplo, si toma una expresión regular simple, probablemente rechazará el siguiente host
http://invalid##host.com
pero permitirá
http://invalid-host.foo
es un host válido, pero no válido si considera los TLD existentes. De hecho, la solución funcionaría si desea validar el nombre de host, no el dominio porque el siguiente es un nombre de host válido
http://host.foo
también el siguiente
http://localhost
Ahora, déjame darte algunas soluciones.
Si desea validar un dominio, debe olvidarse de las expresiones regulares. La mejor solución disponible en este momento es la Lista de sufijos públicos, una lista mantenida por Mozilla. PublicSuffix una biblioteca de Ruby para analizar y validar dominios en contra de la Lista de sufijos públicos, y se llama PublicSuffix .
Si desea validar el formato de un URI / URL, entonces quizás quiera usar expresiones regulares. En lugar de buscar uno, use el método Ruby URI.parse
.
require ''uri''
def valid_url?(uri)
uri = URI.parse(uri) && !uri.host.nil?
rescue URI::InvalidURIError
false
end
Incluso puede decidir hacerlo más restrictivo. Por ejemplo, si desea que la URL sea una URL HTTP / HTTPS, puede hacer que la validación sea más precisa.
require ''uri''
def valid_url?(url)
uri = URI.parse(url)
uri.is_a?(URI::HTTP) && !uri.host.nil?
rescue URI::InvalidURIError
false
end
Por supuesto, hay muchas mejoras que puede aplicar a este método, incluida la comprobación de una ruta o un esquema.
Por último, pero no menos importante, también puede empaquetar este código en un validador:
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
Y como un módulo
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
Y luego solo include UrlValidator
en cualquier modelo para el que quieras validar url''s. Solo incluyendo opciones.
https://github.com/perfectline/validates_url es una gema agradable y simple que hará prácticamente todo por usted