是否将http(s)添加到URL?

问题描述 投票:33回答:7

我正在模型中使用此正则表达式来验证用户提交的URL。我不想强迫用户键入http部分,但如果它不存在,我想自己添加它。

validates :url, :format => { :with => /^((http|https):\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+).[a-z]{2,5}(:[0-9]{1,5})?(\/.)?$/ix, :message => " is not valid" }

任何想法我该怎么做?我对验证和正则表达式的经验很少。

ruby-on-rails ruby regex validation url
7个回答
70
投票

如果不存在,请使用前置过滤器添加:

before_validation :smart_add_url_protocol

protected

def smart_add_url_protocol
  unless self.url[/\Ahttp:\/\//] || self.url[/\Ahttps:\/\//]
    self.url = "http://#{self.url}"
  end
end

保留验证信息,这样,如果他们输入错误,他们就可以纠正协议。


37
投票

不要使用正则表达式,请使用URI.parse将其拆开,然后查看URL上是否存在方案:

URI.parse

使用URI库还可以很容易地清理可能有人试图将其放入URL的任何无用的废话(例如userinfo)。


5
投票

接受的答案很好。但是,如果字段(url)是可选的,则可能会引发错误,例如u = URI.parse('/pancakes') if(!u.scheme) # prepend http:// and try again elsif(%w{http https}.include?(u.scheme)) # you're okay else # you've been give some other kind of # URL and might want to complain about it end 类的undefined method +。以下应解决该问题:

nil

4
投票

根据mu的回答,这是我在模型中使用的代码。它在保存:link而不需要模型过滤器时运行。需要Super才能调用默认的保存方法。

def smart_add_url_protocol
  if self.url && !url_protocol_present?
    self.url = "http://#{self.url}"
  end
end

def url_protocol_present?
  self.url[/\Ahttp:\/\//] || self.url[/\Ahttps:\/\//]
end

4
投票

序言,理由以及应如何做

[当人们在def link=(_link) u=URI.parse(_link) if (!u.scheme) link = "http://" + _link else link = _link end super(link) end 挂钩中更改型号时,我讨厌它。然后,有一天,由于某种原因,需要使用save(validate:false)将模型持久化,那么某些假定一直在分配的字段上运行的过滤器将无法运行。当然,通常要避免拥有无效数据,但是如果不使用该选项,则无需如此。另一个问题是,每次您从模型询问是否有效,这些修改也会发生。简单地询问模型是否有效可能导致模型被修改的事实只是意料之外的,甚至是不希望的。如果我必须选择一个钩子,那就去before_validation钩子。但是,这对我来说不会这样做,因为我们为模型提供了预览视图,并且由于永远不会调用该挂钩,因此这会破坏预览视图中的URI。为此,我认为最好将概念分为模块或关注点,并为用户提供一种“猴子补丁”的好方法,以确保更改字段值始终通过添加默认协议的过滤器运行(如果存在)缺少。

模块

before_save

在您的模型中

#app/models/helpers/uri_field.rb
module Helpers::URIField
  def ensure_valid_protocol_in_uri(field, default_protocol = "http", protocols_matcher="https?")
    alias_method "original_#{field}=", "#{field}="
    define_method "#{field}=" do |new_uri|
      if "#{field}_changed?"
        if new_uri.present? and not new_uri =~ /^#{protocols_matcher}:\/\//
          new_uri = "#{default_protocol}://#{new_uri}"
        end
        self.send("original_#{field}=", new_uri)
      end
    end
  end
end

关注

[如果出于某种原因,您宁愿使用Rails Concern模式,可以很容易地将上述模块转换为关注模块(使用方式与之完全相同,只不过您使用了extend Helpers::URIField ensure_valid_protocol_in_uri :url #Should you wish to default to https or support other protocols e.g. ftp, it is #easy to extend this solution to cover those cases as well #e.g. with something like this #ensure_valid_protocol_in_uri :url, "https", "https?|ftp"

include Concerns::URIField

P.S。以上方法已通过Rails 3和Mongoid 2进行了测试。PPS如果您发现此方法的重新定义和别名太神奇了,您可以选择不覆盖该方法,而是使用虚拟字段模式,非常类似于密码(虚拟,可批量分配)和encrypted_pa​​ssword(永久保留,不可批量分配)并使用sanitize_url(虚拟的,可批量分配)和url(永久保存,不可大规模分配)。


2
投票

使用某些上述正则表达式,这是一种方便的方法,用于覆盖模型上的默认url(例如,如果您的ActiveRecord模型具有'url'列)

#app/models/concerns/uri_field.rb
module Concerns::URIField
  extend ActiveSupport::Concern

  included do
    def self.ensure_valid_protocol_in_uri(field, default_protocol = "http", protocols_matcher="https?")
      alias_method "original_#{field}=", "#{field}="
      define_method "#{field}=" do |new_uri|
        if "#{field}_changed?"
          if new_uri.present? and not new_uri =~ /^#{protocols_matcher}:\/\//
            new_uri = "#{default_protocol}://#{new_uri}"
          end
          self.send("original_#{field}=", new_uri)
        end
      end
    end
  end
end

0
投票

我不会在验证中尝试这样做,因为它实际上不是验证的一部分。

让验证(可选)进行检查;如果他们搞砸了,那将是一个验证错误,这很好。

如果还没有协议,请考虑使用回调(def url _url = read_attribute(:url).try(:downcase) if(_url.present?) unless _url[/\Ahttp:\/\//] || _url[/\Ahttps:\/\//] _url = "http://#{_url}" end end _url end after_create等)在协议之前添加前缀。

((我对其他答案投了赞成票;我认为它们都比我的要好。但这是另一个选择:)

© www.soinside.com 2019 - 2024. All rights reserved.