はじめに
最近 Rails に触れる機会があり、Strong Parameters について調べた際にタイトルの内容が知りたかったのですが、書かれている記事を見つけられなかったのでそのことを。
Ruby 自体も含め、しっかり絡んだことがなかったので用語などが Ruby 界隈と合っていなかったりしたらご容赦ください。
先に Strong Parameters について簡単に触れますが、不要な場合は「Strong Parameters が不要なパターン」からお読みください。
Strong Parameters とは
詳しいところは検索すればいろいろ出てくるので割愛しますが、ざっくりいうと
Post されたデータ( params )に対し、マスアサインメントを利用する際に意図しないパラメーターが使用されないようにする仕組み
です。
マスアサインメントは、複数カラムを一括で指定する仕組み。
元々はモデル側で attr_accessible
を使って許可を与えていたようですが、脆弱性を作りやすいことから Rails4 で Strong Parameters が導入されたようです。
このため、Rails4 では attr_accessible
が廃止されています。
Strong Parameters の例
Person
に name
, age
, isAdmin
あったとして、isAdmin
を更新させたくない場合、
def create
@person = Person.new(person_params)
@person.save
end
private
def person_params
params.require(:person).permit(:name, :age)
end
という感じで、name
と age
だけ許可を与えます。
@person = Person.new(params.require(:person).permit(:name, :age))
という書き方もできますが、Strong Parameters を処理する部分は private
にすることが推奨されています(可読性、視認性もよくないですし)。
参考: https://railsguides.jp/action_controller_overview.html#strong-parameters
Strong Parameters を使用しなかった場合、どうなるか
マスアサインメントを利用しつつ、Strong Parameters を使用しなかった場合、ForbiddenAttributesError
が発生します。
ForbiddenAttributesError
Post されたデータは params
という ActionController::Parameters のインスタンスに格納されていますが、この params
には permitted: false
というのが付与されていて、このままマスアサインメントに利用すると「許可されていない(permitted: false
)」、つまり「禁止されている(Forbidden)」ということでエラーになります。
前述の例のように permit
を行うことで permitted: true
に変わり、マスアサインメントに利用できるようになります。
Strong Parameters が不要なパターンは?
本題です。
Strong Parameters が不要なパターンはあるのだろうか、というのが気になったのですが、そのことに触れている記事や書き込みを見つけることができませんでした。
マスアサインメントを使わなければ不要なんだろう、というのは推察できますが、明確に書かれたものがない以上自分で確認するしかありません。
ということで以下のパターンで確認しました。
Strong Parameters が不要なパターン
いずれもマスアサインメントを利用していないため、permit
をしなくてもエラーになりません。
パターン1:それぞれのパラメーターを引数で指定する( params を直接使用)
def create
@person = Person.new(name: params[:person][:name], age: params[:person][:age])
@person.save
end
パターン2:それぞれのパラメーターを引数で指定する( params の値を変数に格納)
def create
name = params[:person][:name]
age = params[:person][:age]
@person = Person.new(name: name, age: age)
@person.save
end
パターン3:Person オブジェクトの各パラメーターに代入
def create
@person = Person.new
@person.name = params[:person][:name]
@person.age = params[:person][:age]
@person.save
end
パターン4:params[:person] をいったん変数に格納後、個別に設定
def create
person = params[:person]
@person = Person.new(name: person[:name], age: person[:age])
@person.save
end
パターン5:params[:person] の各値を別のハッシュに格納し、引数に使用
def create
person = {}
person[:name] = params[:person][:name]
person[:age] = params[:person][:age]
@person = Person.new(person)
@person.save
end
このパターンはマスアサインメントを使用していることになりますが、person
は ActionController::Parameters
のインスタンスではないため permitted
属性がついておらず、permit
をしなくてもエラーになりません。
エラーになるパターン
一応、エラーになるパターンも書いておきます。
これらのパターンでは ForbiddenAttributesError
が発生します。
パターン1
def create
@person = Person.new(params[:person])
@person.save
end
パターン2
def create
person = params[:person]
@person = Person.new(person)
@person.save
end
この場合でも person
に permitted: false
がついたままなのでエラーになります。
パターン3
def create
para = ActionController::Parameters.new({
name: "", age: -1
})
para[:name] = params[:person][:name]
para[:age] = params[:person][:age]
@person = Person.new(para)
@person.save
end
params
からは個別に取り出していますが、 para
の方にも permitted: false
がついているので permit
しないとエラーになります。
おまけ
本末転倒パターン
def create
@person = Person.new(person_params)
@person.save
end
private
def person_params
params.require(:person).permit(:name, :age, :isAdmin)
end
Strong Parameters を使おうと使うまいと、余計なパラメーターを指定しては元も子もないので注意したいところ。
マスアサインメントを使用したほうがコード量が少なく、 person_params
を見ればどれが許可されているかもわかりやすいので、可能な限り Strong Parameters を使用したほうがいいかもしれません。