如何在 Rails API 模型上应用验证版本控制?

How to apply validation versioning on Rails API models?

我正在搜索有关构建 Rails API 以及如何在模型验证上正确应用某些版本控制的信息。

假设有这样的模型和路线:

class Person < ApplicationRecord
  validates :name, presence: true
end
namespace :v1
  resources :people
end

因此,我的模型正在验证 name 是否存在。

但是,如果我想发布我的 API 的新版本,并在 Person 上进行新的验证,例如,job 字段是强制性的,就像这个:

class Person < ApplicationRecord
  validates :name, presence: true
  validates :job, presence: true
end

如果我这样做,我会破坏我的 V1 端点。那么,是否有一种好的做法可以在不破坏我以前的 V1 端点的情况下实现它?还是我应该将此验证从模型中删除并在请求参数级别进行验证?

我看到一些 gems 在模型级别制作它,但我想知道是否有一些模式,而不使用任何额外的 gem。

实现此目的的最简单方法是在验证中使用 :on 选项并执行上下文验证,例如

class Person < ApplicationRecord
  validates :name, presence: true
  validates :job, presence: true, on: :v2
end

然后您可以在 v1 端点中使用 person.valid? 并在 v2 端点中使用 person.valid?(:v2)

但此解决方案仅适用于简单场景,无法在长期或更复杂的应用程序中扩展。

更好的解决方案是提取验证逻辑以形成从您的参数创建的对象,并为包含一些重大更改的端点的每个版本提供单独的表单对象,例如:

class PersonV1Form
  include ActiveModel::Validations

  attr_accessor :name, :job
  validates :name, presence: true

  def initialize(params = {})
    @name = params[:name]
    @job = params[:job]
  end
end

class PersonV2Form
  include ActiveModel::Validations

  attr_accessor :name, :job
  validates :name, :job, presence: true

  def initialize(params = {})
    @name = params[:name]
    @job = params[:job]
  end
end

然后在您的控制器中,您可以在使用表单对象为您的模型提供数据之前验证表单对象:

module V1
  class PeopleController < ApplicationController
    def create
      person = PersonV1Form.new(params[:person])
      if person.valid?
        # ...
      end
    end
  end
end

module V2
  class PeopleController < ApplicationController
    def create
      person = PersonV2Form.new(params[:person])
      if person.valid?
        # ...
      end
    end
  end
end