Rails 8 を触っていて、コントローラのストロングパラメータに params.expect という見慣れないメソッドが出てきて「これ、今までの params.require(:article).permit(...) と何が違うの?」と戸惑っていませんか?
rubocop-rails が Rails/StrongParametersExpect で「expect に書き換えろ」と警告してくるのに、言われたとおり直したら今まで動いていたフォームが急に通らなくなった——そんな報告も出ています。この記事では、params.expect が require/permit と何が違うのか、どう書き換えるのか、そしてネスト属性でハマる「ダブルブラケット」問題までを、実務で使う前提で整理します。
まず結論:1行で置き換えて、より厳格になる
細かい話は後にして、先に結論から渡します。params.expect は require と permit を1行にまとめたもので、さらに「期待した構造と違うパラメータが来たら弾く」という安全側の挙動が加わります。
![params.require(:article).permit(:title, :body)がRails 8のparams.expect(article: [:title, :body])に1行で書き換わる比較図。記述量が2段から1行に、不正な形は500から400 Bad Requestになる。](https://toaru-kaihatsu.com/wp-content/uploads/2026/10/params-expect-before-after.png)
# Before(Rails 7まで)
params.require(:article).permit(:title, :body, :published)
# After(Rails 8)
params.expect(article: [:title, :body, :published])ポイントは2つです。require(:article) が消えて article: というキーに吸収されること、そして許可する属性を配列 [ ] で囲むことです。この「配列で囲む」が、後で説明するネスト属性のハマりどころに直結します。
params.expectとは:require/permitとの違い
従来の require(:article).permit(...) には、地味に困る弱点がありました。悪意のあるリクエストが article を「ハッシュではなく文字列」で送ってきたとき、permit が意図しない挙動になり、サーバーエラー(500)につながるケースがあったのです。
params.expect は、この「期待した型・構造で来ているか」まで検証します。構造が違えば ActionController::ParameterMissing を発生させ、Rails がこれを400 Bad Requestとして返します。500(サーバーの不具合)ではなく400(リクエストが不正)として扱えるのが、実務的に大きな違いです。
| 観点 | require + permit | expect(Rails 8) |
|---|---|---|
| 記述量 | require(:x).permit(...) の2段 | expect(x: [...]) の1行 |
| 構造の検証 | キーの有無が中心 | ハッシュ/配列の形まで検証 |
| 不正な形が来たとき | 500 になりうる | 400 Bad Request |
| 属性の書き方 | permit(:title) | x: [:title](配列で囲む) |
ざっくり言えば、「短く書けて、より厳しくチェックしてくれる」のが params.expect です。新規に書くコントローラなら、基本はこちらを選んで問題ありません。
書き換え手順:3つのパターンを押さえる
実際の書き換えは、パラメータの形ごとに3パターンを覚えれば足ります。順番に見ていきましょう。
パターン1:単純な属性だけ
一番多い形です。require(:model).permit(:a, :b) を、expect(model: [:a, :b]) に置き換えます。
def article_params
# Before
# params.require(:article).permit(:title, :body, :published)
# After
params.expect(article: [:title, :body, :published])
endパターン2:単一のネストしたハッシュ
belongs_to の相手や、1件だけのネスト構造(author: { name:, email: } のような形)は、ハッシュをそのまま入れ子にします。ここはブラケット1個のままで大丈夫です。
params.expect(article: [:title, :body, author: [:name, :email]])パターン3:has_manyのネスト属性(ここが本番)
accepts_nested_attributes_for :comments のような複数件のネスト属性が、最大のハマりどころです。Rails のネストフォームは comments_attributes を「インデックス付きの配列(=複数のハッシュ)」として送ってきます。これを受けるには、ブラケットを2重 [[ ]]にする必要があります。
# Before
# params.require(:article).permit(
# :title, comments_attributes: [:id, :body, :_destroy]
# )
# After(comments_attributes を [[ ]] で囲む)
params.expect(article: [
:title,
comments_attributes: [[:id, :body, :_destroy]]
])ダブルブラケット[[ ]]の意味
なぜ [ ] と [[ ]] を使い分けるのか。ここさえ理解すれば params.expect はほぼ怖くありません。違いは「単一のハッシュを期待するか、ハッシュの配列を期待するか」です。
![params.expectのブラケット[ ]と[[ ]]の使い分け図。[ ]は単一ハッシュ(belongs_to)、[[ ]]はハッシュの配列(has_manyのネスト属性)を期待する。[ ]で書くとネスト部分がnilになり保存されない注意。](https://toaru-kaihatsu.com/wp-content/uploads/2026/10/params-expect-brackets.png)
| 書き方 | 期待する形 | 使う場面 |
|---|---|---|
key: [:a, :b] | 単一のハッシュ { a:, b: } | トップレベルの属性 / belongs_to |
key: [[:a, :b]] | ハッシュの配列 [{ a:, b: }, ...] | has_many のネスト属性 |
外側の [ ] が「これは配列ですよ」という宣言、内側の [ ] が「各要素に許可する属性のリスト」です。複数件まとめて送られてくるものは [[ ]]、と覚えておけば判断を間違えません。
ハマり逆引き:ここでつまずく
ネスト属性が「エラーも出ずに」消える
一番やっかいなのが、has_many のネスト属性を [ ] 1個で書いてしまったケースです。配列を期待すべきところに単一ハッシュの指定をすると、そのネスト部分だけが黙って nil になり、保存されません。例外は飛ばないので、「なぜか子レコードだけ保存されない」という形で表面化します。
対処はシンプルで、複数件のネスト属性は必ず [[ ]] にすること。「保存したはずの子レコードが無い」ときは、まずここを疑ってください。
rubocopの自動修正が、動いているコードを壊す
rubocop-rails の Rails/StrongParametersExpect は require/permit を expect に書き換えるよう促しますが、自動修正(autocorrect)が既存の動くコードを壊す可能性が報告されています。特に前述のネスト属性まわりで、単純な機械置換ではブラケットの数まで面倒を見てくれないことがあります。
自動修正を一括でかけるのではなく、ネスト属性を含むコントローラは1つずつ動作確認しながら直すのが安全です。
expectとexpect!を混同する
よく似た expect! もありますが、挙動が違います。expect は失敗時に ActionController::ParameterMissing を発生させ、Rails が400を返してくれるのに対し、expect! は ActionController::ExpectedParameterMissing を発生させ、400レンダリングは行いません。通常のコントローラで使うのは expect の方です。
移行の線引き:全部を今すぐ置き換えなくていい
「require/permit はもう使えないの?」と不安になるかもしれませんが、そんなことはありません。require/permit は引き続き使えます。だからこそ、無理に一括置換しないのが現実的な判断です。
- 新規のコントローラ:最初から
params.expectで書く。短く、安全側に倒せる - 既存の単純なパラメータ:余裕があれば
expectに寄せる。リスクは低い - 既存のネスト属性つきコントローラ:挙動差を理解した上で、テストを通しながら段階的に。急がない
「新しい書き方が出た=全部直さなきゃ」ではなく、壊れると困る箇所ほど慎重に。これは params.expect に限らず、Rails のバージョンアップ全般に効く構えです。
まとめ
params.expect は、require/permit を1行にまとめつつ、パラメータの構造まで検証して不正な形を400で弾いてくれる、安全側に倒したストロングパラメータです。最後に要点を整理します。
params.require(:x).permit(:a, :b)→params.expect(x: [:a, :b])has_manyのネスト属性はダブルブラケット[[ ]]。単一ハッシュは[ ]のまま[ ]で書くとネスト部分が黙って消えるので要注意- rubocop の自動修正は過信せず、ネスト属性は1つずつ確認
require/permitも当面使える。新規はexpect、既存は段階移行で十分
フォームまわりをもう少し広く押さえたい方は、rails ブログ機能の実装方法7選でフォーム・投稿の実例を、パラメータを絞り込む設計の例としてはDeviseをカスタマイズしてプロフィール更新とパスワード変更を分ける方法もあわせてどうぞ。Rails の環境構築から始めたい場合はmacOSにRuby on Railsをインストールが入り口になります。
スポンサーリンク
