RailsをAPIモードで使用していた時に400などのエラーリスポンスを書く必要が出てきた。複数のアクション内で同じようなエラー処理になってしまうだろうなあと思ったのでこのような処理を共通化する方法はないだろうかと調べた時にrescue_from
メソッドに出会った。
Github
メソッドの仕組み
Githubの処理を見ていくと、3つの引数をとっている。
klasses
特定のエラーに対応するクラス名を受け取る。。クラス名そのままでもいいし(クラスとして定義されている定数?)、文字列でも良い。
with
エラーを補足した際に行うための処理を渡す。メソッドかprocオブジェクト。
&block
エラーを補足した際に行うためのブロック。
with引数がnilだった場合にこのブロックがwithに代入される。
最後にrescue_handlers
にクラス名とエラー処理の2つを要素とした2次元配列を代入している。
使い方
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
render plain: "404 Not Found", status: 404
end
end
以下のようにカスタム例外クラスも補足できる。
class ApplicationController < ActionController::Base
rescue_from User::NotAuthorized, with: :user_not_authorized
private
def user_not_authorized
flash[:error] = "You don't have access to this section."
redirect_back(fallback_location: root_path)
end
end
class ClientsController < ApplicationController
# ユーザーがクライアントにアクセスする権限を持っているかどうかをチェックする
before_action :check_authorization
# このアクション内で認証周りを心配する必要がない
def edit
@client = Client.find(params[:id])
end
private
# ユーザーが認証されていない場合は単に例外をスローする
def check_authorization
raise User::NotAuthorized unless current_user.admin?
end
end
特定のコントローラだけで行いたい場合はモジュール化してそれを必要とするコントローラ内でインクルードさせるとよい。
module ErrorRenderable
extend ActiveSupport::Concern
included do
rescue_from ActiveRecord::RecordNotFound do |e|
render json: { errors: { title: 'レコードが見つかりません', detail: 'IDと一致するレコードが見つかりません' } }, status: :not_found }
end
end
end
class UsersController < ApplicationController
include ErrorRenderable
def show
@user = User.find(params[:id])
end
end