TL;DR
StrongParametersとは
StrongParameters は, require と permit からなる Action Controller のパラメータです。
何ができるんだろう
ユーザから意図しない構造で渡ってきたパラメータに対して、コントローラおよび早期の段階で例外として処理できます。
require
: 指定した値が存在しなければ例外を出す
permit
: 許可してない構造のオブジェクトをパラメータから除外する
何が嬉しいんだろう
コントローラの先(モデル、ファクトリ、リポジトリなどなど)で値の構造に関する例外について考えなくてよくなります。
つまり、StrongParametersを通していれば、以降受け取った値そのものだけにフォーカスできるんです。
都度、もし値がArrayじゃなかったらどうしようとか、Hashの構造違ったらどうしようとか考えなくていい、優しい世界になるはず!
実践
require
require
は必須項目のようなものです。
つまり、 require
を利用すると、必要とする指定のパラメータのみを取り出すことができます。
例えば、パラメータから要求した対象の値だけをとりだすことができます。
received_json = {
name: 'Bob',
books: [
{ book_code: 0, title: 'Title_0' },
{ book_code: 1, title: 'Title_1' },
{ book_code: 2, title: 'Title_2' },
],
}
params = ActionController::Parameters.new(received_json)
params.require(:name)
# => 'Bob'
params.require(:books)
# => [
# {"code"=>0, "title"=>"Title_0"},
# {"code"=>1, "title"=>"Title_1"},
# {"code"=>2, "title"=>"Title_2"},
# ]
必要とする指定のパラメータがなければ、もちろん例外(ParameterMissing
)を出します。
params.require(:age)
# => ActionController::ParameterMissing: param is missing or the value is empty: age
permit
permit
はホワイトリストのようなものです。
つまり、permit
を利用すると、意図した構造以外を除外することが出来ます。
例えば下記の場合、name 以外のパラメータは意図した構造外の値になるので除外されます。
received_json = {
name: 'Bob',
books: [
{ book_code: 0, title: 'Title_0' },
{ book_code: 1, title: 'Title_1' },
{ book_code: 2, title: 'Title_2' },
],
}
params = ActionController::Parameters.new(received_json)
params.permit(:name)
# Unpermitted parameters: books
# => {"name"=>"Bob"}
permit
は構造を保証してくれるものなので、下記のような books 単体では除外されます。
一見すると上手くいってくれそうなものですけれどもね…
params.permit(:books)
# Unpermitted parameters: name, books
# => {}
仮に、例に上げた books のような構造を許可したい時には下記のようになります。
params.permit(:books => [:book_code, :title])
# Unpermitted parameters: name
# => {
# "books"=>[
# {"book_code"=>0, "title"=>"Title_0"},
# {"book_code"=>1, "title"=>"Title_1"},
# {"book_code"=>2, "title"=>"Title_2"}
# ]
# }
例に上げた params のような構造を許可したい場合は下記のようになります。
params.permit(:name, :books => [:book_code, :title])
# => {
# "name"=>"Bob",
# "books"=>[
# {"book_code"=>0, "title"=>"Title_0"},
# {"book_code"=>1, "title"=>"Title_1"},
# {"book_code"=>2, "title"=>"Title_2"}
# ]
# }
しれっと書きましたが、実は上記のような書き方はおすすめできないです。
アロー演算子は後方にある要素全てを指し示すため、意図しない値まで差されて暴れることがあるためです。
params.permit(:books => [:book_code, :title], :name)
# => SyntaxError: unexpected ')', expecting =>
なので、Object単位でカーリーブレイスでカバーしてあげましょう。
params.permit({:books => [:book_code, :title]}, :name)
# => {
# "name"=>"Bob",
# "books"=>[
# {"book_code"=>0, "title"=>"Title_0"},
# {"book_code"=>1, "title"=>"Title_1"},
# {"book_code"=>2, "title"=>"Title_2"}
# ]
# }