IT story

Rails : 링크 (URL)를 확인하는 좋은 방법은 무엇입니까?

hot-time 2020. 7. 12. 09:34
반응형

Rails : 링크 (URL)를 확인하는 좋은 방법은 무엇입니까?


Rails에서 URL을 가장 잘 검증하는 방법이 궁금합니다. 정규 표현식을 사용하려고 생각했지만 이것이 최선의 방법인지 확실하지 않습니다.

그리고 정규식을 사용한다면 누군가 나에게 제안 할 수 있습니까? 나는 여전히 정규식을 처음 사용합니다.


URL 확인은 까다로운 작업입니다. 또한 매우 광범위한 요청이기도합니다.

정확히 무엇을하고 싶습니까? URL 형식, 존재 여부 또는 무엇을 확인 하시겠습니까? 수행하려는 작업에 따라 여러 가지 가능성이 있습니다.

정규식은 URL 형식을 확인할 수 있습니다. 그러나 복잡한 정규 표현식조차도 유효한 URL을 처리하도록 보장 할 수 없습니다.

예를 들어 간단한 정규식을 사용하면 다음 호스트를 거부합니다.

http://invalid##host.com

그러나 그것은 허용합니다

http://invalid-host.foo

기존 TLD를 고려할 경우 유효한 호스트는 아니지만 유효한 호스트입니다. 실제로 다음과 같은 호스트 이름이므로 도메인이 아닌 호스트 이름의 유효성을 검사하려는 경우 솔루션이 작동합니다.

http://host.foo

뿐만 아니라 다음 중 하나

http://localhost

이제 몇 가지 해결책을 알려 드리겠습니다.

도메인의 유효성을 검사하려면 정규식을 잊어야합니다. 현재 사용 가능한 최상의 솔루션은 Mozilla가 유지 관리하는 목록 인 공개 접미사 목록입니다. 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

내 모델 안에 하나의 라이너를 사용합니다.

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 보석 (단지 멋진 래퍼 Addressable::URI.parse솔루션).

그냥 추가

gem 'validate_url'

당신의 Gemfile다음 모델에서 할 수 있습니다

validates :click_through_url, url: true

이 질문은 이미 답변되었지만 도대체 내가 사용하는 솔루션을 제안합니다.

정규 표현식은 내가 만난 모든 URL에서 잘 작동합니다. setter 메소드는 프로토콜이 언급되지 않은 경우주의해야합니다 (http : //로 가정).

마지막으로 페이지를 가져 오려고합니다. HTTP 200뿐만 아니라 리디렉션도 허용해야합니다.

# 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

scheme없이 URL을 허용하고 domain zone 및 ip-hostnames를 확인하는 valid_url gem을 시도 할 수도 있습니다 .

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을 다음과 같이 지원하고 있습니다.

^와 $를 사용하면 Rails 유효성 검사기에서이 경고 보안을 볼 수 있으므로 A와 Z의 사용에 주목하십시오.

 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을 확인해야했지만) 유니 코드 URL의 추가 요구 사항 (예 :)에 대처해야했습니다 http://кц.рф...

몇 가지 솔루션을 연구하고 다음을 발견했습니다.

  • 가장 먼저 제안되는 것은를 사용하는 것입니다 URI.parse. 자세한 내용은 Simone Carletti의 답변을 확인하십시오. 이것은 정상적으로 작동하지만 유니 코드 URL에는 적합하지 않습니다.
  • 내가 본 두 번째 방법은 Ilya Grigorik의 방법입니다. http://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails/ 기본적으로 그는 URL; 작동하면 유효합니다 ...
  • 내가 찾은 세 번째 방법 (및 내가 선호하는 방법)은 stdlib 대신 gem을 URI.parse사용하는 것과 비슷 하지만 접근 방식 입니다. 이 접근법은 여기에 자세히 설명되어 있습니다 : http://rawsyntax.com/blog/url-validation-in-rails-3-and-ruby-in-general/addressableURI

다음은 David James가 게시 한 유효성 검사기 의 업데이트 버전입니다 . 그것은 Benjamin Fleischer에 의해 출판되었습니다 . 한편, 나는 여기 에서 찾을 수있는 업데이트 된 포크를 밀었다 .

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

다음은 예제를 다루는 gem문제입니다addressable .


I use a slight variation on lafeber solution above. It disallows consecutive dots in the hostname (such as for instance in www.many...dots.com):

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

URI.parse seems to mandate scheme prefixing, which in some cases is not what you may want (e.g. if you want to allow your users to quickly spell URLs in forms such as twitter.com/username)


I have been using the 'activevalidators' gem and it's works pretty well (not just for urls validation)

you can find it here

It's all documented but basically once the gem added you'll want to add the following few lines in an initializer say : /config/environments/initializers/active_validators_activation.rb

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

(Note : you can replace :all by :url or :whatever if you just want to validate specific types of values)

And then back in your model something like this

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

Now Restart the server and that should be it


You can validate multiple urls using something like:

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

https://github.com/perfectline/validates_url is a nice and simple gem that will do pretty much everything for you


Recently I had this same issue and I found a work around for valid 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

The first part of the validate_url method is enough to validate url format. The second part will make sure the url exists by sending a request.


If you want simple validation and a custom error message:

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

And as a module

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

And then just include UrlValidator in any model that you want to validate url's for. Just including for options.


URL validation cannot be handled simply by using a Regular Expression as the number of websites keep growing and new domain naming schemes keep coming up.

In my case, I simply write a custom validator that checks for a successful response.

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

I am validating the path attribute of my model by using record.path. I am also pushing the error to the respective attribute name by using record.errors[:path].

You can simply replace this with any attribute name.

Then on, I simply call the custom validator in my model.

class Url < ApplicationRecord

  # validations
  validates_presence_of :path
  validates_with UrlValidator

end

You could use regex for this, for me works good this one:

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

I liked to monkeypatch the URI module to add the valid? method

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

참고URL : https://stackoverflow.com/questions/7167895/rails-whats-a-good-way-to-validate-links-urls

반응형