选择在哪里放置验证?

Choosing where to put validations?

考虑来自 the "Getting Started" guide 的这个片段:

module Web::Controllers::Books
  class Create
    include Web::Action

    expose :book

    params do
      param :book do
        param :title,  presence: true
        param :author, presence: true
      end
    end

    def call(params)
      if params.valid?
        @book = BookRepository.create(Book.new(params[:book]))

        redirect_to '/books'
      end
    end
  end
end

请注意 titleauthor 上的验证,它们存在于控制器操作中。我的问题是:为什么这些验证是针对操作参数而不是 Book 实体?也就是说,假设验证 Book,你可以这样写:

def call(params)
  book = Book.new(params)
  if book.valid?
    @book = BookRepository.create(Book.new(params[:book]))

    redirect_to '/books'
  end
end

并完全摆脱 params 块。这对我来说似乎更自然,并且会促进跨不同操作更容易地重用验证。

params 方法是否有我没有发现的优势?对 Book 实体进行验证是否有缺点?

官方指南中的 Validations & Coercion 部分解释了为什么你应该对你的请求进行验证,而不是对你的模型进行验证。

总结一下,主要有以下两个原因:

  1. 从架构的角度来看,永远不应允许无效输入进入您的系统,因此最好在控制器级别完全跳过它们,而不是创建一个仅用于验证的模型,因为这是相当昂贵的操作。

  2. 可以有多个请求在同一模型上工作。如果您在模型级别进行验证,您还需要考虑这些请求的不同场景,这也是控制器的责任,而不是模型的责任。

不过,如果您的业务逻辑中可以使用上述场景,那将归结为个人喜好问题。