使用IP地址rails validates_format_of

时间:2018-02-20 03:10:59

标签: ruby-on-rails ruby activemodel

我允许用户在输入字段中输入IP地址,输入字段可能是逗号分隔的不同类型,例如(无特定顺序):

192.168.1.1,192.168.2.1-25,10.10.10.0/24,192.168.1.2

此“字符串”将保存在device.ips_to_scan下的数据库中。

我想validates_format_of这些,但我觉得编写一个似乎在rails中运行的正则表达式有点困难,而它确实适用于regex101(https://regex101.com/r/nf2bnM/1):

 validates_format_of :ips_scan, with: /\A([0-9]{1,3}\.){3}[0-9]{1,3}(\/([1-2][0-9]|[0-9]|3[0-2]))?(-([0-9]{1,3}))?,?\Z/i, on: :update

预计会失败:

Started PUT "/devices/2" for 127.0.0.1 at 2018-02-19 22:03:15 -0500
Processing by DevicesController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"EQCFG6/xoJHtP6Nd3oqaYRW6mypfEoCMrnio1yj6loP+KtvjgLZ9Gmhb0oTwCjD0RGH+qQuctZFVIvF5HBJcGw==", "device"=>{"ips_scan"=>"192.168.1.1,192.168.2.1-25,a.b.c.d", "ips_exclude"=>"10.10.10.1"}, "commit"=>"Save", "id"=>"2"}
  User Load (0.8ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 2], ["LIMIT", 1]]
  Device Load (1.6ms)  SELECT  "devices".* FROM "devices" WHERE "devices"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
   (0.5ms)  BEGIN
  User Load (0.7ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
   (0.5ms)  ROLLBACK
Redirected to http://localhost:3000/devices/2/edit
Completed 302 Found in 47ms (ActiveRecord: 12.1ms)

......但这个应该有效:

Processing by DevicesController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"JJfmT/0l5MEDc+gUH/WHHp3bbgyzjGa0xTzaXM3E/WHLvbi30mI5SoYXmc0xdS2LzAALj+cCU6k+ZoPy+Sw3+Q==", "device"=>{"ips_scan"=>"192.168.1.1,192.168.2.1-25,192.168.1.2", "ips_exclude"=>"10.10.10.1"}, "commit"=>"Save", "id"=>"2"}
  User Load (0.9ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 2], ["LIMIT", 1]]
  Device Load (0.7ms)  SELECT  "devices".* FROM "devices" WHERE "devices"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
   (0.6ms)  BEGIN
  User Load (0.7ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
   (0.6ms)  ROLLBACK
Redirected to http://localhost:3000/devices/2/edit
Completed 302 Found in 17ms (ActiveRecord: 3.5ms)

我能想到的最后一点是,我确实有很强的参数,但我允许ips_scan,所以这不应该是一个问题:

def update
  if @device.update(device_params)
    flash[:notice] = 'Successful update'
    respond_with :edit, :device
  else
    flash[:warning] = 'Unable to update'
    respond_with :edit, :device
  end
end

private def device_params
  params.require(:device).permit(:token, :ips_scan, :ips_exclude)
end

我希望你的rubyist有一个雄辩的解决方案。想到的第一个想法是我必须split字符串,并按顺序检查每个元素以确保它匹配。

2 个答案:

答案 0 :(得分:0)

虽然我仍然对模型中的一个很好的单行内容持开放态度,但我能够通过创建一个问题来解决这个问题:

  

模型/关切/ ip_validator.rb

class IpValidator < ActiveModel::Validator
  def validate(record)
    ips = record.ips_scan.split(',')
    ips.each do |ip|
      /([0-9]{1,3}\.){3}[0-9]{1,3}(\/([1-2][0-9]|[0-9]|3[0-2]))?(-([0-9]{1,3}))?/ =~ ip
      record.errors.add(:ips_scan, ' is not valid') unless $LAST_MATCH_INFO
    end
  end
end

我的模型中的调用现在看起来像:

validates :ips_scan, :ips_exclude, ip: true, on: :update

答案 1 :(得分:0)

您可以在自定义验证程序中使用此方法来检查IP地址

require 'ipaddr'

def valid_ip_addr?(ip_addr)
  IPAddr.new(ip_addr)
  true
rescue IPAddr::InvalidAddressError => _error
  false
end