【问题标题】:Validate UUID string in ruby/rails在 ruby​​/rails 中验证 UUID 字符串
【发布时间】:2017-11-27 10:32:49
【问题描述】:

我正在开发一个 API。为了获得更好的开发人员体验,我想向用户报告params 的任何容易发现的问题。我的代码验证字符串、整数、布尔值、iso8601 日期和域特定的值列表。我正在寻找一种方法来验证字符串是否是有效的 UUID。我正在研究可能的选择。

【问题讨论】:

  • 您可以使用正则表达式验证 uuid 的格式。见stackoverflow.com/questions/7680771/…
  • PostgreSQL 适配器对 UUID 进行了一些验证,您可以检查实现并在您的模型中使用。 github.com/rails/rails/blob/master/activerecord/lib/…
  • 感谢@NarenSisodiya,我应该在问题中提到验证不直接作用于类的属性,因此我不确定如何使用它。此外,我们的 rails 项目正在使用 Sequel。不过,底层正则表达式的提示很有用。

标签: ruby-on-rails ruby validation uuid


【解决方案1】:

基于使用正则表达式的普遍建议:

def validate_uuid_format(uuid)
  uuid_regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
  return true if uuid_regex.match?(uuid.to_s.downcase)

  log_and_raise_error("Given argument is not a valid UUID: '#{format_argument_output(uuid)}'")
end

请注意,这只检查字符串是否符合8-4-4-4-12 格式并忽略任何版本检查。

【讨论】:

【解决方案2】:

虽然我的回答会稍微限制问题的一般性,但我希望它仍然足够有趣。此限制是假设您根据要检查的参数集实例化一个新对象,开始验证,然后返回错误对象,除非为 nil。

# params[:lot] = { material_id: [SOME STRING], maybe: more_attributes }
lot = Lot.new params[:lot]
lot.valid?

这样您就可以使用 Rails 的内置验证机制。但是,截至 2020 年 5 月,似乎仍然不支持将属性的格式验证为 UUID。对于原生,我的意思是:

# models/lot.rb
# material_id is of type string, as per db/schema.rb
validates :material_id,
  uuid: true

在 Rails 6.0.3 中键入此内容:

ArgumentError (Unknown validator: 'UuidValidator')

因此,将属性验证为 UUID 的关键是生成一个 UuidValidator 类,并确保 Rails 的内部能够自然地找到和使用它。

受 coderwall.com 的 Doug Puchalski 的解决方案 suggested 的启发,结合Rails API docs,我想出了这个解决方案:

# lib/uuid_validator.rb
class UuidValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value =~ /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
      msg = options[:message] || "is not a valid UUID"
      record.errors.add(attribute, msg)
    end
  end
end

现在,假设您实例化了一个新的 Lot 实例并错误地将一个整数作为外键分配给 material_id:

lot = Lot.new({material_id: 1})
lot.material_id
=> "1" # note the auto type cast based on schema definition
lot.valid?
=> false
lot.errors.messages
=> {:material_id=>["is not a valid UUID"]}
# now, assign a valid uuid to material_id
lot.material_id = SecureRandom.uuid
=> "8c0f2f01-8f8e-4e83-a2a0-f5dd2e63fc33"
lot.valid?
=> true

重要
只要将属性的数据类型更改为 uuid,

# db/schema.rb
create_table "lots", id: false, force: :cascade do |t|
  #t.string "material_id"
  t.uuid "material_id"
end

Rails 6 将自动只接受有效的 uuid 来分配给 material_id。当尝试分配除有效 UUID 字符串之外的任何内容时,它会优雅地失败:

lot = Lot.new
# trying to assign an integer...
lot.material_id({material_id: 1})
# results in gracious failure
=> nil
# the same is true for 'nearly valid' UUID strings, note the four last chars
lot.material_id = "44ab2cc4-f9e5-45c9-a08d-de6a98c0xxxx"
=> nil

但是,您仍然会得到正确的验证响应:

lot.valid?
=> false
lot.errors.messages
=> {:material_id=>["is not a valid UUID"]}

【讨论】:

    【解决方案3】:

    你可以使用原生的UUID.validate(my_string)类方法。

    https://www.rubydoc.info/gems/uuid/2.3.1/UUID#validate-class_method

    请注意,它匹配多种 UUID 格式。

    【讨论】:

    • 你能举例说明如何应用它,而不仅仅是一个链接吗?
    • 这是一个简单的类方法,它返回一个布尔值:UUID.validate(my_string)。 (已编辑)
    • "Native" 暗示它是 Ruby 标准库的一部分。 UUID 不是 Ruby 的一部分。您在此处链接了一个在 Ruby 之上提供此类的 gem。
    【解决方案4】:

    使用正则表达式匹配器对其进行验证,这取决于您要验证的 UUID 版本。我确信每个 UUID 版本的正则表达式模式都有大量资源。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-14
      • 2016-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-19
      相关资源
      最近更新 更多