星期三,2012 年 3 月 21 日

强参数:在控制器中而不是模型中处理批量分配

投稿人:dhh

我们正在探索一种处理 Rails 中批量分配保护的新方法。或者说,它实际上并非一种新方法,它更像是在一些醋 (在你忘记的时候加进去的) 中提取了已确立的做法。这种新方法在默认情况下将成为 Rails 4.0 的一部分,但我们希望在该版本发布之前就获得你的帮助来测试和形成这种方法。

强参数

这种新方法是对 slice 模式 的提取,而且我们为其命名的插件为 strong_parameters (也已经 以 gem 的形式提供)。基本理念是将批量分配保护从模型移动到控制器中,而控制器正是批量分配保护所属的地方。

控制器的全部意义在于控制用户和应用程序之间的流程,包括身份验证、授权,作为该访问控制的一部分。我们绝不应该将批量分配保护放到模型中,而且许多人很久之前就停止这样做,转而使用 slice 模式或其变体。现在是时候提取该模式并将其提供给用户了。

范例

class PeopleController < ActionController::Base
  # This will raise an ActiveModel::ForbiddenAttributes exception because it's using mass assignment
  # without an explicit permit step.
  def create
    Person.create(params[:person])
  end
  
  # This will pass with flying colors as long as there's a person key in the parameters, otherwise
  # it'll raise a ActionController::MissingParameter exception, which will get caught by 
  # ActionController::Base and turned into that 400 Bad Request reply.
  def update
    redirect_to current_account.people.find(params[:id]).tap do |person|
      person.update_attributes!(person_params)
    end
  end
  
  private
    # Using a private method to encapsulate the permissible parameters is just a good pattern
    # since you'll be able to reuse the same permit list between create and update. Also, you
    # can specialize this method with per-user checking of permissible attributes.
    def person_params
      params.required(:person).permit(:name, :age)
    end
end

我们仍在琢磨 API,但它现在已经很好用了,而且我在新的 Basecamp 中用 strong parameters 的 permit 方法替换了我们自己的 slice 模式。

有待完成的工作

我们仍在努力寻找一种处理嵌套参数的整洁方法,但我们已经准备好设计以供实施,所以应该不会太遥远。此外,Yehuda 将致力于表单签名,以减轻在标准 HTML 表单情况下手动声明允许参数的必要性(你仍需要对 API 和其他客户端手动使用 permit 参数)。

但它现在已经足够好用了。该插件当前仅与 rails/3-2-stable 发布版本 275ee0dc7b 及更高版本以及 rails/master 发布版本 b49a7ddce1 及更高版本完全兼容,这是由于存在与封装参数相关的测试问题(如果你不为自己的 json API 使用封装参数,那么你可以在任何版本的 Rails 3.2 中使用该插件)。