Railsではエラーが発生したときに実行する処理にrescueを使うことが多いです。rescueはRailsではなくRubyの制御構文です。
Railsにはrescueとは別に、rescue_from
という便利な例外処理があります。
rescure_fromを使えば、指定した例外毎に処理をまとめることができます。ここでは、rescue_fromの処理内容や使い方について解説しています。
rescue_fromとは?
rescue_fromはRailsの機能で例外処理をとりまとめることができます。
もう少し詳しく解説すると、同じく例外処理であるrescueは、ある特定の処理に対してエラーが発生したときの処理を記述するものです。
なので、処理が分かれている場合は、各処理毎にrescueを記述する必要があります。
例えば、Railsで存在しないDBのテーブルにアクセスしようとするとActiveRecord::RecordNotFoundというエラーが発生します。
コントローラの中で、index, action, edit,,,など複数のアクションがあるので、rescueを使ってこのエラーが発生したときの処理を書こうとすると、アクションの中の処理の数だけrescueを記述しなければいけません。
対して、rescue_fromは例外の種類を指定し、そのときに実行する処理を記述しておけば、1つのコントローラファイルの中のすべてのアクションで発生する例外をキャッチしてくれます。
このため、rescue_fromは複数のアクションを記述するコントローラの中で重宝される便利な例外処理になります。
rescue_fromの基本構文
rescue_fromの書き方は大きく2つあります。
- withオプションで例外時に実行するメソッドを指定する
- ブロックで例外処理を記述する
withオプションで例外時に実行するメソッドを指定する
rescue_fromでキャッチする例外を指定して、withオプションで例外時に実行するメソッドを指定します。(※メソッド名はシンボルで指定します。「:」を忘れずに)
通常、指定したメソッドはrescue_fromを記述した場所でのみ使用するので、privateとし、外から実行できないようにします。
rescue_from キャッチする例外名 , with: :メソッド名
private
def メソッド名
例外発生時に実行する処理
end
メソッドで例外の内容を受け取る方法
発生した例外の中身を受け取るには、メソッドの引数を指定します。例外の中身は、第1引数に入ります。
rescue_from キャッチする例外名 , with: :メソッド名
private
def メソッド名(変数名)
例外発生時に実行する処理
end
変数名は、errorの省略形であるeが使われることが多いです。
ブロックで例外処理を記述する
rescue_fromの後ろにブロックを記述して、例外発生時の処理を指定することもできます。
rescue_from キャッチする例外名 do |変数名|
例外発生時の処理
end
doの後ろで変数を指定することで、発生したエラーの内容を代入し、処理の中で呼び出すこともできます。
この変数名はexceptionを使うケースが多いです。
rescue_fromの注意点
ExceptionやStandardErrorの指定は禁止
キャッチする例外に、例外クラスの親である、ExceptionやStandardErrorを指定すると、深刻な障害が発生する可能性があります。
Exceptionとはすべての例外の祖先になるクラスです。StandardErrorは一般的な例外クラスの親になるクラスです。
例えばRailsでよく見かけるエラーには、ActiveRecord::RecordNotFound、NoMethodErrorなどがあります。どちらも親クラスとしてStandardError、孫クラスとしてExceptionを継承しています。
▼継承関係
- ActiveRecord::RecordNotFound < StandardError < Exception
- NoMethodError::RecordNo < StandardError < Exception
https://railsguides.jp/action_controller_overview.html
rescue_from
にException
やStandardError
を指定すると、Railsでの正しい例外ハンドリングが阻害されて深刻な副作用が生じる可能性があります。よほどの理由がない限り、この指定はおすすめできません。
とはいえ、rescue_from Exception
を記述しているページを多くみかけます。
rescue_fromは下から優先的に実行する
rescue_fromを複数記述する場合、下にある処理から優先的に実行されます。
このため、 複数のエラーを複合する例外クラスは上に記述する必要があります。
rescue_fromを複数記述する
rescue_fromをエラー毎に分けて複数記述することもできます。
▼実例
よく使われているのは、ActiveRecord::RecordNotFoundとActionController::RoutingErrorを指定する場合です。メソッドには render_404や、render_500 が使われることが多いです。
rescue_from ActiveRecord::RecordNotFound, with: :render_404
rescue_from ActionController::RoutingError, with: :render_404
ActiveRecord::RecordNotFoundとは?
ActiveRecord::RecordNotFoundとは、モデルを使ってDBからデータを取得するときに、存在しないデータを取得しようとした場合に発生するエラーです。
ActiveRecord::RecordNotFoundとは?
ActionController::RoutingErrorとは、アクセスのあったURIに対して、ルーティングが見つからないときに発生するエラーです。
すべてのコントローラで共通の例外処理を作成する
rescure_fromはコントローラの中に記述します。その例外処理は記述したコントローラのアクションで発生した例外のみに対して有効になります。
もし、すべてのコントローラに対して、あるエラーが発生したときに共通した例外処理を実行したい場合は、app > controllers配下にある、application_controller.rbに記述すればOKです。
class ApplicationController < ActionController::Base
rescue_fromの処理を記述
end
rescue_fromの成り立ち
rescue_fromをより詳しく理解するために、rescueで記述した処理をrescue_fromで記述する例を紹介します。
以下の流れがわかれば、rescue_fromは便利だな!となります。
rescueを使った例外処理
まずは、コントローラ内でrescueを使って例外処理を記述する場合です。
例えば、Usersコントローラのindexとshowアクションに対して、DB上の指定したデータ(レコード)が存在しなかった場合に発生するエラーActiveRecord::RecordNotFoundをキャッチして例外処理を記述すると以下のようになります。
class UsersController < ApplicationController
def index
@users = User.all
rescue ActiveRecord::RecordNotFound => e
render json: { error: '404 not found' }, status: 404
end
def show
@user = User.find(params[:id])
rescue ctiveRecord::RecordNotFound => e
render json: { error: '404 not found' }, status: 404
end
この状態では、まったく同じ処理をindexとshowに記述しています。
indexとshow以外に、new, create, edit, update, destroyなどのアクションがあれば、その数だけ記述が増えます。
rescue_fromを使った例外処理(do~end)
上記をrescue_fromを使って書き変えると、コード量が少なくなります。例外処理が1つにまとまるので処理の内容もわかりやすくなります。
class UsersController < ApplicationController
rescue_from ActiveRecord::RecordNotFound do |exception|
render json: { error: '404 not found' }, status: 404
end
def index
@users = User.all
end
def show
@user = User.find(params[:id])
end
end
rescue_fromを使った例外処理(メソッド)
上に処理も記述するとごちゃっとすると感じる場合は、下部にメソッドとして書くこともできます。
class UsersController < ApplicationController
rescue_from ActiveRecord::RecordNotFound, with: :data_not_found
def index
@users = User.all
end
def show
@user = User.find(params[:id])
end
private
def data_not_found(e)
render json: { error: '404 not found' }, status: 404
end
end
rescue_fromを使ったことで例外処理をシンプルに記述することができました。
リファクタリング(補足)
コントローラの場合、showやedit、update、destroyアクションでは、共通して、指定したユーザーの情報を取得する処理を行います。
@user = User.find(params[:id])
そのように、各アクションで共通となる処理はメソッド化するとコードがシンプルになります。
また、ユーザー情報の取得は必ず実行する処理になるのでbefore_actionで、アクション実行前に行うようにします。
これらのリファクタリングを追加すると上記のusers_controller.rbは次のようになります。
class UsersController < ApplicationController
before_action :set_user, only: [ :show, :edit, :update, :destroy ]
rescue_from ActiveRecord::RecordNotFound, with: :data_not_found
def index
@users = User.all
end
def show
end
private
def set_user
@user = User.find(params[:id])
end
def data_not_found(e)
render json: { error: '404 not found' }, status: 404
end
end
最後に、セキュリティ強化のために指定したカラムのデータ以外は受け取らないようにする、ストロングパラメータ化の処理を追加します。
class UsersController < ApplicationController
before_action :set_user, only: [ :show, :edit, :update, :destroy ]
rescue_from ActiveRecord::RecordNotFound, with: :data_not_found
def index
@users = User.all
end
def show
end
private
def set_user
@user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :age)
end
def data_not_found(e)
render json: { error: '404 not found' }, status: 404
end
end
以上でコントローラのリファクタリングは完成です。
ストロングパラメータとは何か?なぜ必要か?具体的にどんな処理をしているか?については下記をご参考ください。
【Rails】超便利コマンドscaffoldの使い方を完全理解 | ストロングパラメータとは?
rescue_fromの実例
rescue_fromの実例をいくつか紹介します。
ブロックで例外処理を記述する
ActiveRecord::RecordNotFoundが発生したときに、「404 not found」と書かれたページを表示することは、次の3行で可能です。
rescue_from ActiveRecord::RecordNotFound do |exception|
render json: { error: '404 not found' }, status: 404
end
renderでjsonオプションを使うと、指定した内容をJSON形式で表示することができます。また、statusオプションでステータスコードを指定すれば、そのステータスコードでページを開くことができます。
▼ブラウザの表示例
ステータスコードも404として認識されます。
エラーの内容を表示する
上記の例で、エラーの内容追加して表示させるには、指定した変数を使います。
この変数の中にエラーの詳細が代入されています。
rescue_from ActiveRecord::RecordNotFound do |exception|
render json: { error: '404 not found', エラーの詳細: exception }, status: 404
end
▼ブラウザの表示
メソッドで例外処理を記述する(リダイレクトとフラッシュメッセージの表示)
エラーが発生したときに、指定したページにリダイレクトし、フラッシュメッセージを表示します。
rescue_from ActiveRecord::RecordNotFound, with: :data_not_found
def data_not_found
flash[:error] = "データが存在しません"
redirect_back(fallback_location: root_path)
end
ActiveRecord::RecordNotFoundが発生したら、data_not_foundメソッドを実行します。
flash[:error]
flashメッセージのキー名errorに指定したデータを格納します。flashページが表示された初回のみメッセージを表示する機能です。
ビューファイルで <%= flash[:error] %>
とすれば、エラーメッセージがある場合のみ表示をします。
redirect_back(fallback_location: root_path)
redirect_back(fallback_location: root_path)
は、戻るページがあるときにはそのページに戻り、戻るページがない(ブラウザのキャッシュに残っていない)ときはトップページに戻る処理です。
root_pathはrootに_pathヘルパーを使った形で、トップページの相対パスになります。
▼ブラウザの表示