【Rails入門説明書】paramsについて解説
はじめに
Railsの学習を進めていく中で、ディレクトリやファイルの構成、モデルやコントローラ、Viewの関係などは、みなさん注目しています。
しかし、プログラムの要であるデータの入出力については、案外さらりと流してしまっているだけの人もいるのではないでしょうか?
Railsは他のプログラム言語と違ってフレームワークが暗黙のうちに作り出した変数などがあり、プログラムコードを読むだけでは理解が難しい部分もあります。
じつは、今回解説するparamsも、それらフレームワークが暗黙のうちに作り出したものの1つなのです。
そんなparamsについて、詳しく説明していきます。
paramsによるデータの受け取り
paramsにはハッシュ形式でデータが格納されており、Railsのページ遷移で情報をやりとりするときに使われています。
つまり、paramsを理解することで、Railsアプリケーションのページ遷移時のデータの流れを理解できるということです。プログラムはデータを扱う処理がほとんどですので、データの流れが分かれば、それはプログラムを理解したといっても過言ありません。
paramsはそれほど重要な、Railsの要なのです。
Railsアプリケーションへのアクセスというのは、基本的にはいくつかのリクエストに分けられます。
それらのリクエストのパターンをまとめて、パターンごとの動作を整理しているのがroutes.rbファイルです。
routes.rbの詳細は、「【Rails入門説明書】routesについて解説」で説明していますので、詳細は割愛しますが、今回はルーティングで呼び出される側の説明だと思えばイメージしやすいのではないでしょうか。
データの格納場所
Webアプリケーションの弱点は、ページ遷移するときの情報のやりとりにいくつかの手法が存在することです。
それは、URLの一部にデータを載せる方法やクエリ(URLの末尾に?を付けた後に記載して直接渡す方法)、POSTでのデータ送信など、できることもやり方も違う方法が存在しているのです。
そして、このどれかを選ぶのではなく、このすべてに対応することで、ユーザーの利便性が上がります。そのため、Webアプリケーションの作成は、それぞれのデータのやりとりについて検討し、実装しなければいけません。
Railsでは、そんな面倒なデータのやりとりの方法を意識することなく、データをハッシュ形式のparamsで送受信することができるのです。
環境構築
Railsのparamsを使ったデータのやりとりについては、ただ説明を読むよりも、実際に動きを見ながら手を動かした方が圧倒的に分かりやすくなります。
そのため、まずはテスト環境を作っておきましょう。
[bash]rails new params_test
cd params_test
rails generate scaffold User name:string age:integer
rake db:migrate
rails c
User.create(name:”Ryosuke”,age:18)
User.create(name:”Daigo”,age:23)
User.create(name:”Hiroshi”,age:35)
exit
[/bash]
次に、リクエストの内容を確認できるように、ログを仕込んでおきます。(loggerについては、「【Rails入門説明書】loggerについて解説」を参照してください)
(app/controllers/application_controller.rb)
class ApplicationController < ActionController::Base
before_action :http_header_log
private
def http_header_log
logger.info(“Request: #{request.headers}”)
end
end
https://web-camp.io/magazine/archives/12654
GETリクエスト
Railsアプリケーションへのもっとも単純なアクセスが、GETリクエストによるアクセスでしょう。
このとき、GETリクエストとともに渡すURLにパラメータを含めて渡すことができ、そのパラメータをparamsを経由して取得することができます。
具体的な動作を見てみましょう。
テスト環境のサーバを起動し、ブラウザのURL欄に「localhost:3000/users/1」と入力してみてください。
ここでURLの末尾にある「1」がパラメータです。このパラメータが「1」であるため、1つめに登録されたデータが表示されています。
ログも確認してみましょう。(以降、ログは必要な部分だけ抜粋して表示しています)
[bash]Started GET “/users/1” for 0.0.0.0 at YYYY-mm-DD HH:MM:SS +0900
Processing by UsersController#show as HTML
Parameters: {“id”=>”1”}
[/bash]
パラメータが「Parameters: {“id”=>”1”}」として渡されていることが分かります。
つまり、アクセスするURLを「/users/2」に変更すれば、「2」が渡されて、2つめに登録した「Daigo」が表示されることになるわけです。
(ログ)
[bash]Started GET “/users/2” for 0.0.0.0 at YYYY-mm-DD HH:MM:SS +0900
Processing by UsersController#show as HTML
Parameters: {“id”=>”2”}
[/bash]
この動作を理解するため、まずは「rails routes」コマンドで、ルーティングを確認してみましょう。
[bash]Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
:
[/bash]
今回アクセスした「/users/1」は、以下のルーティングにマッチします。
[bash]user GET /users/:id(.:format) users#show
[/bash]
ちょうど、「:id」のところに「1」を記載してアクセスしていますので、「Parameters: {“id”=>”1”}」になると考えれば、分かりやすいのではないでしょうか。
では次に、このルーティングによって実行されるコントローラのアクションメソッド「users#show」を確認しましょう。
(app/controllers/users_controller.rb)
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
:
def show
end
:
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
end
:
showメソッドそのものは何もしていませんので、処理の実体は、「before_action :set_user, only: [:show, :edit, :update, :destroy]」によって実行されているset_userメソッドです。
set_userメソッドは、「params[:id]」で絞り込んだレコードを取得して、@userへ代入しています。
そして、この「params[:id]」が、ログの「Parameters: {“id”=>””}」にあたり、「/users/1」でアクセスした場合は、「@user = User.find(1)」というコードと同等になるわけです。
※なお、「{“id”=>”1”}」というログの通り、取得できるのは文字列です。もし数値として処理するのであれば、「to_i」などで変換しなければいけませんので気を付けましょう。
クエリ
多くのサイトで使用されている「クエリ」でのパラメータ渡しについても、paramsを利用して取得することができるようになっています。
Viewファイルを少し修正して、確認してみましょう。
(app/views/users/show.html.erb)
<%= notice %>
<strong>Name:</strong>
<%= @user.name %>
<strong>Age:</strong>
<%= @user.age %>
<strong>Query val:</strong><%= params[:val] %>
<!– (1) –>
<%= link_to ‘Edit’, edit_user_path(@user) %> |
<%= link_to ‘Back’, users_path %>
(1)の1行を追記します。
その上で、ブラウザのURLに「http://localhost:3000/users/2?val=query_val」を入力してアクセスしてみましょう。
(ログ)
[bash]Started GET “/users/2?val=query_val” for 0.0.0.0 at YYYY-mm-DD HH:MM:SS +0900
Processing by UsersController#show as HTML
Parameters: {“val”=>”query_val”, “id”=>”2”}
[/bash]
このように、クエリを使っても、同じようにparamsにパラメータがハッシュ形式で設定されていることが分かります。
https://web-camp.io/magazine/archives/12481
POSTリクエスト
POSTリクエストは、フォームからの入力で「送信」ボタンを押した場合などに発生するリクエストです。
そのため、POSTリクエストは、GETリクエストより多くのパラメータを送信することになります。
GETリクエストの場合と同様に、動作を確認してみましょう。
POSTの場合は、新規登録画面で「Create User」ボタンを押すところから見ていきます。
(localhost:3000/users/new)にアクセスして、フォームに入力して「Create User」ボタンを押します。
(「Create User」ボタン押下時のログ)
[bash]Started POST “/users” for 127.0.0.1 at 2018-11-06 22:03:27 +0900
Processing by UsersController#create as HTML
Parameters: {.., “user”=>{“name”=>”Keito”, “age”=>”25”}, ..}
:
[/bash]
対象のルーティングは以下です。
[bash]POST /users(.:format) users#create
[/bash]
つまり、実行されるコントローラのアクションメソッドは、「users#create」になります。プログラムコードを確認しましょう。
(app/controllers/users_controller.rb)
:
def create
@user = User.new(user_params)
(省略)
end
:
def user_params
params.require(:user).permit(:name, :age)
end
:
createメソッドでUserモデルをnewで生成しています。そのときに渡されている引数がparamsであり、ログに出力されている「{“name”=>”Keito”, “age”=>”25”}」なのです。
ここでは、user_paramsメソッドが渡されていますが、その戻り値は[カラム名=>値,..]ですので、paramsと同じものになります。
例えば、以下のようにログを追記して確認すれば、同じであることが確認できるでしょう。
(app/controllers/users_controller.rb)
:
def create
logger.info(“user_params : #{params.require(:user).permit(:name, :age)}”)
logger.info(“params : #{params[:user]}”)
@user = User.new(user_params)
(省略)
end
:
(ログ)
[bash]:
user_params : {“name”=>”Keito”, “age”=>”25”}
params : {“name”=>”Keito”, “age”=>”25”}
:
[/bash]
ストロングパラメータ
user_paramsとparams[:user]が同じであれば、以下のように修正しても動作するように思えるのではないでしょうか。
(app/controllers/users_controller.rb)
:
def create
# @user = User.new(user_params)
@user = User.new(params[:user])
(省略)
end
:
しかし、この修正を行って実行してみると、次のような「ActiveModel::ForbiddenAttributesError 」という例外が発生してしまうのです。
これは、Rails4から導入された「ストロングパラメータ」というセキュリティ対策のためで、テーブルに登録、更新するすべてのデータは、このストロングパラメータによる検証をクリアしなければ、登録できなくなっているのです。
そのため、直接paramsを与えても、上記のような例外が発生して、登録することができなくなっています。
この検証内容は、user_paramsメソッドの内容の通りです。
(app/controllers/users_controller.rb)
:
def user_params
params.require(:user).permit(:name, :age)
end
:
user_paramsメソッドに記載のある1行は、「paramsが:userというキーを持っていて、かつparams[:user]が、:nameと:ageというキーを持つハッシュである」ことを確認しています。
そして、許可されたキーのハッシュが戻り値として返されます。
つまり、ストロングパラメータは、「モデルに含まれる登録可能なカラムを特定し、それ以外のカラムへのアクセスを禁止するしくみ」といえるでしょう。
ストロングパラメータの存在理由
そもそも、なぜこのように面倒な手続きを踏む必要があるかというと、それは「Mass-assignment」という脆弱性への対策です。
「Mass-assignment」というのは、テーブルにあるが、本来はユーザーが更新することができないカラム(user_idや識別子、削除フラグなど)を、フォームの入力欄にスクリプトタグなどを使って更新することができる脆弱性です。
ストロングパラメータの検証を利用することで、許可されたカラムしか登録、更新することができませんので、この問題を回避することができます。
“未経験”でもたった1ヶ月で営業からエンジニアとして転職!『WebCamp』受講者インタビュー
まとめ
今回は、paramsについて説明しました。
あまり目立った機能ではないかもしれませんが、paramsは、Railsの動作の要ともいえるものです。
そのため、paramsを理解するということは、Railsアプリケーションのデータの流れを理解するということになり、paramsを意識することで、Railsアプリケーションの理解がより深まります。
ただし、データの流れは、そのままでは目で見て確認することができませんので、今回紹介したように、loggerを活用して確認することをおすすめします。
(loggerについては、「【Rails入門説明書】loggerについて解説」を参照してください)
・paramsはデータのやりとりに使われるハッシュ
・paramsはハッシュ形式でデータを保持する
・paramsが使われるシーンは、ページ遷移時
・GETリクエストのクエリなどのパラメータを取得できる
・POSTリクエストのデータを取得できる
・POSTリクエストで受け取ったデータをそのままテーブルへ登録すると、「Mass-assignment」のリスクがあるため、必ずストロングパラメータの検証を行わなければいけない