Quantcast
Channel: 例外タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 100

Rails によるカスタム例外の設定とエラーハンドリング

$
0
0

Rails で例外を発生させたい際は,raise...つまり RuntimeError をよく使用するかと思います。

しかし,サービス上の制約から,特定の状況下で例外を発生させる場合,raiseだけでは物足りなくなる時があります。
raiseでは「何かまずいことが起きてしまいました!」程度のことしか伝えてくれません。まぁ,引数に渡す message を見れば理解できるかもですが...

兎にも角にも,特定の状況下に対する例外が存在するなら,その例外に対して名前を付けてあげましょう。

カスタム例外を設定すると,発生時に「何に対する例外か」がパッと理解できるようになりますし,特定の動作に誘導することも容易になりますので,良いことづくめです!

□ 本文

■ 前提情報

使用するアプリケーション

Railsチュートリアルで作成する SampleApp における、users_controller のusers#editに着目して実装します。

app/controllers/users_controller.rb
classUsersController<ApplicationControllerbefore_action:correct_user,only: [:edit,:update]#...private#...# 正しいユーザーかどうか確認defcorrect_user# GET   /users/:id/edit# PATCH /users/:id@user=User.find(params[:id])redirect_to(root_url)unlesscurrent_user?(@user)end#...end

カスタム例外の設定規則

  • StarndardErrorを継承する
  • クラス名の末尾にErrorを付ける

■ カスタム例外の設定方法

実装自体は,とても単純なのですが,設定場所にいくつか種類がありますので,紹介していきます。

実装例1: 発生ファイルに直接設定

その名の通り,例外が発生するファイル自身に設定します。

特定のクラスに強く結びつける方法であることから,Model 層や Service 層で見かけたりします。

app/controllers/users_controller.rb
classUsersController<ApplicationControllerclassNotPermittedError<StandardError;endbefore_action:correct_user,only: [:edit,:update]#...private#...defcorrect_user@user=User.find(params[:id])raiseNotPermittedError,"あなたにリクエスト権限がありません"unlesscurrent_user?(@user)end#...end

実装例2: app/ 配下に設定

自作の module を app/ 配下に設定します。

validators や services と同じ考え方で配置する感じですかね。

app/errors/application_error.rb
moduleApplicationErrorclassNotPermittedError<StandardError;endend
app/controllers/users_controller.rb
classUsersController<ApplicationControllerbefore_action:correct_user,only: [:edit,:update]#...private#...defcorrect_user@user=User.find(params[:id])raiseApplicationError::NotPermittedError,"あなたにリクエスト権限がありません"unlesscurrent_user?(@user)end#...end

実装例3: lib/ 配下に設定

lib ディレクトリの存在目的から見ると,王道パターンかも。

なお,lib 直下の配置が気になる場合,適宜ディレクトリを挟んで設定してください。

config/application.rb
#...Bundler.require(*Rails.groups)# ↓ 追加コードrequire_relative'../lib/exception.rb'moduleSampleApp#...end
lib/exception.rb
moduleApplicationclassError<StandardError;endclassNotPermittedError<Error;endend
app/controllers/users_controller.rb
classUsersController<ApplicationControllerbefore_action:correct_user,only: [:edit,:update]#...private#...defcorrect_user@user=User.find(params[:id])raiseApplicationError::NotPermittedError,"あなたにリクエスト権限がありません"unlesscurrent_user?(@user)end#...end

■ エラーハンドリング

さて,これでカスタム例外は作成完了ですが,仕上げが残っています。

このままでは,例外を発生させたままです。
ユーザ側から見ると 500 エラー画面が出てきて,なぜ強制終了したのか理由がわかりませんし,サービスの操作感として連続性が失われるのも避ける必要があります。

元のコードでは,不正なアクセスをしたユーザに対して,root_url にリダイレクトさせていますので,カスタム例外が発生した際は,同じようにリダイレクトさせましょう。

また、例外を握り潰さないために、サーバ側に理由を説明するためのログを残しましょう。

app/controllers/application_controller.rb
classApplicationController<ActionController::Base#...rescue_fromApplication::NotPermittedError,with: :redirect_root_pagedefredirect_root_pageRails.logger.info"ルート URL にリダイレクト: #{exception.message}"ifexceptionredirect_toroot_url,flash: {danger: "閲覧権限がありません"}end#...end

※ シンプルに表記することを目的として application_controller.rb に記入していますが,色々追加されるファイルでもあるため,concerns に切り出すと尚可読性が高まるでしょう。

□ 余談

OSS におけるカスタム例外の設定方法も調べてみると,見事にバラバラだったので,プロジェクト毎に設定方法が異なるかもしれない :thinking:

具体的な命名も設定場所も異なるため、プロジェクトに合わせて、柔軟に対応しましょう。

今回使用した PR です→カスタム例外の設定 by masayuki-0319 · Pull Request #9 · masayuki-0319/sample_app


Viewing all articles
Browse latest Browse all 100

Trending Articles