Rails の Strong Parameters が不要なパターン

# はじめに

最近 Rails に触れる機会があり、Strong Parameters について調べた際にタイトルの内容が知りたかったのですが、書かれている記事を見つけられなかったのでそのことを。

Ruby 自体も含め、しっかり絡んだことがなかったので用語などが Ruby 界隈と合っていなかったりしたらご容赦ください。

先に Strong Parameters について簡単に触れますが、不要な場合は「[Strong Parameters が不要なパターン](#strong-parameters-が不要なパターン)」からお読みください。

# Strong Parameters とは

詳しいところは検索すればいろいろ出てくるので割愛しますが、ざっくりいうと

**Post されたデータ( params )に対し、マスアサインメントを利用する際に意図しないパラメーターが使用されないようにする仕組み**

です。

マスアサインメントは、複数カラムを一括で指定する仕組み。

元々はモデル側で `attr_accessible` を使って許可を与えていたようですが、脆弱性を作りやすいことから Rails4 で Strong Parameters が導入されたようです。
このため、Rails4 では `attr_accessible` が廃止されています。

## Strong Parameters の例

`Person` に `name`, `age`, `isAdmin` あったとして、`isAdmin` を更新させたくない場合、

```Ruby:people_controller.rb
def create
    @person = Person.new(person_params)
    @person.save
end

private
def person_params
    params.require(:person).permit(:name, :age)
end
```

という感じで、`name` と `age` だけ許可を与えます。

```Ruby
@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 を直接使用)

```Ruby:people_controller.rb
def create
    @person = Person.new(name: params[:person][:name], age: params[:person][:age])
    @person.save
end
```

## パターン2：それぞれのパラメーターを引数で指定する( params の値を変数に格納)

```Ruby:people_controller.rb
def create
    name = params[:person][:name]
    age = params[:person][:age]
    @person = Person.new(name: name, age: age)
    @person.save
end
```

## パターン3：Person オブジェクトの各パラメーターに代入

```Ruby:people_controller.rb
def create
    @person = Person.new
    @person.name = params[:person][:name]
    @person.age = params[:person][:age]
    @person.save
end
```

## パターン4：params[:person] をいったん変数に格納後、個別に設定

```Ruby:people_controller.rb
def create
    person = params[:person]
    @person = Person.new(name: person[:name], age: person[:age])
    @person.save
end
```

## パターン5：params[:person] の各値を別のハッシュに格納し、引数に使用

```Ruby:people_controller.rb
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

```Ruby:people_controller.rb
def create
    @person = Person.new(params[:person])
    @person.save
end
```

## パターン2

```Ruby:people_controller.rb
def create
    person = params[:person]
    @person = Person.new(person)
    @person.save
end
```

この場合でも `person` に `permitted: false` がついたままなのでエラーになります。

## パターン3

```Ruby:people_controller.rb
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` しないとエラーになります。

# おまけ

## 本末転倒パターン

```Ruby:people_controller.rb
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 を使用したほうがいいかもしれません。