読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

Rails4でフォームに入力された値を取得するStrongParameters機能

Rails初級 Rails Controller

Rails4以降で導入されたフォームで入力された値のマスアサイメントを防ぐためのStrongParametersという機能について説明します。

動作確認

  • Rails 4.1

目次

  1. Railsのparamsメソッド
  2. StrongParameterのメソッド


1. Railsのparamsメソッド

Railsではname属性には次のような規約があるため、コントローラー内のparamsメソッドで値をネストされたハッシュとして取得できます。

  • product[name]のように、name属性に[名前]をつけると、ネストしたハッシュとして受け取れる
  • product[category_ids][]のように、name属性に複数空の[]をつけると、配列として値を受け取れる。

例えば、次のような商品情報を登録するフォームから入力を受け付けたとします。

<form action="/products" method="post">
  <div style="display:none">
    <input name="utf8" type="hidden" value="✓">
    <input name="authenticity_token" type="hidden" value="gpATAhefgIhzK7cuGsWVuKGZCE4gJeEbrMv+a/wOJ+I=">
  </div>

  <div class="field">
    <label for="product_name">Name</label><br>
    <input id="product_name" name="product[name]" type="text">
  </div>
  <div class="field">
    <label for="product_description">Description</label><br>
    <textarea id="product_description" name="product[description]"></textarea>
  </div>
  <div class="field">
    <label for="product_price">Price</label><br>
    <input id="product_price" name="product[price]" type="number">
  </div>
  <div class="field">
    <label for="product_category">Category</label><br>
    <input id="product_category_idx_1" name="product[cateogory_ids][]" type="checkbox" value="1"> カテゴリ1
    <input id="product_category_idx_2" name="product[cateogory_ids][]" type="checkbox" value="2"> カテゴリ2
    <input id="product_category_idx_3" name="product[cateogory_ids][]" type="checkbox" value="3"> カテゴリ3
  </div>

  <div class="actions">
    <input name="commit" type="submit" value="Create Product">
  </div>
</form>

このとき、paramsは次のような値を返します。

params
#=> {
#     "utf8"=>"✓",
#     "authenticity_token"=>"gpATAhefgIhzK7cuGsWVuKGZCE4gJeEbrMv+a/wOJ+I=",
#     "product" => {
#         "name"=>"オレンジジュース",
#         "description"=>"おいしーいオレンジジュース",
#         "price"=>"100"
#         "cateogory_ids" => ["1", "2", "3"]
#     },
#     "commit"=>"Create Product",
#     "action"=>"create",
#     "controller"=>"products"
#   }


2. StrongParameterのメソッド

ハッシュで入力パラメーターを受け取れると、モデルのnewupdateなどのメソッドにそのまま受け渡すことが可能です。

params # => { "name" => "田中太郎", "email" => "taro@test.com", password" => "password" }
User.create(params) # => 新しいユーザーが作成される

しかし、セキュリティの問題として、悪意あるユーザーが次のように入力パラメーターを付加して送ってきた場合、予期しないデータまで更新されてしまいます。

params # => { "name" => "田中太郎", "email" => "taro@test.com", password" => "password", "admin" => "true" }
User.create(params) # => 新しいユーザーが管理者権限でで作成されてしまう

このため、Rails3では、モデル内で次のようにattr_accessibleを使って明示的に記載することで、更新できる値を制限していました。

# Rails3
class Product < ActiveRecord::Base
  attr_accessible :name, :email, :password
end

しかし、書かないと制限できないという性質上、制限し忘れということが有名なサイトなどでも発見されて、デフォルトで許可というのは良くないということになりました。
そのため、Rails4からはデフォルトで拒否し、更新したい値を許可するという形になりました。
それが、StrongParameterという機構であり、paramsの受け取る値(更新を許可する値)をコントローラー側で明示的に記載するようなりました。

paramsメソッドにrequirepermitを使うことで値を許可することができます。

  def create
    @product = Product.new(product_params)

    if @product.save
      redirect_to @product, notice: 'Product was successfully created.'
    else
      render :new
    end
  end


  private
    # StrongParameter
    # productのname, description, priceのみ返される(許可する)
    #
    # paramsの内容
    # => {
    #      "utf8"=>"✓",
    #      "authenticity_token" => "gpATAhefgIhzK7cuGsWVuKGZCE4gJeEbrMv+a/wOJ+I=",
    #      "product"=>
    #         { "name"=>"111",
    #           "description"=>"22",
    #           "price"=>"100",
    #           "cateogory_ids"=>["1", "2"]
    #         },
    #      "commit"=>"Create Product",
    #      "action"=>"create",
    #      "controller"=>"products"
    #     }
    #
    # 返り値
    # => {"name"=>"商品名", "description"=>"商品の説明", "price"=>"100"}
    def product_params
      params.require(:product).permit(:name, :description, :price)

      # 配列も取得したい場合は、次のように記載します。
      # params.require(:product)
      #       .permit(:name, :description, :price, :cateogory_ids => [])
    end


以上です。

参考文献