【Rails】rescue_fromとは?わかりやすく解説。例外処理の便利な使い方実例(rescueとの違い)

rails-prograshi(プロぐらし)-kv Rails
記事内に広告が含まれていることがあります。
[PR]

Railsではエラーが発生したときに実行する処理にrescueを使うことが多いです。rescueはRailsではなくRubyの制御構文です。

Railsにはrescueとは別に、rescue_fromという便利な例外処理があります。

rescure_fromを使えば、指定した例外毎に処理をまとめることができます。ここでは、rescue_fromの処理内容や使い方について解説しています。

あわせて読みたい

rescue_fromの処理は、例外処理の基本形であるrescueやraiseを理解しておくと、より分かりやすくなります。

rescueやraiseなどの処理の詳細については下記をご参考ください。

【Rails】例外を発生させる方法|rescueの使い方。begin rescueとの違いやraise, retry, ensure


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つあります。

  1. withオプションで例外時に実行するメソッドを指定する
  2. ブロックで例外処理を記述する

withオプションで例外時に実行するメソッドを指定する

rescue_fromでキャッチする例外を指定して、withオプションで例外時に実行するメソッドを指定します。(※メソッド名はシンボルで指定します。「:」を忘れずに)

通常、指定したメソッドはrescue_fromを記述した場所でのみ使用するので、privateとし、外から実行できないようにします。

rescue_from キャッチする例外名 , with: :メソッド名


private

   def メソッド名
     例外発生時に実行する処理
   end
tips

privateを記述すると、そこから下の処理に記述する処理はすべてprivateメソッドになります。

privateメソッドであることがわかりやすいように、privateの下に記述する処理はインデントさせます。

publicに戻したい場合は、publicを記述します。

メソッドで例外の内容を受け取る方法

発生した例外の中身を受け取るには、メソッドの引数を指定します。例外の中身は、第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

rescue_fromExceptionStandardErrorを指定すると、Railsでの正しい例外ハンドリングが阻害されて深刻な副作用が生じる可能性があります。よほどの理由がない限り、この指定はおすすめできません。

https://railsguides.jp/action_controller_overview.html

とはいえ、rescue_from Exceptionを記述しているページを多くみかけます。

rescue_fromは下から優先的に実行する

rescue_fromを複数記述する場合、下にある処理から優先的に実行されます。

このため、 複数のエラーを複合する例外クラスは上に記述する必要があります。


rescue_fromを複数記述する

rescue_fromをエラー毎に分けて複数記述することもできます。

注意点

複数指定時は下側のrescue_fromが優先されます。複数のエラーを複合する例外クラスは上に記述する必要があります。

▼実例

よく使われているのは、ActiveRecord::RecordNotFoundActionController::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に対して、ルーティングが見つからないときに発生するエラーです。

注意点

ブラウザ上にエラーページを表示するときは、ステータスコードを404や500にする必要があります。

ステータスコードが200のままで、ページの内容にエラーページを出すことはソフト404と呼ばれ、SEO的にNGです。


すべてのコントローラで共通の例外処理を作成する

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ヘルパーを使った形で、トップページの相対パスになります。

▼ブラウザの表示


(参考)Railsガイド rescue_from

タイトルとURLをコピーしました