💬
【Rails】rescue_fromでエラークラスを一括でCatchする
rails apiで発生した例外をメソッド毎ではなく、定義と継承によって一括で処理するコードを解説します。
rescue_from
-
rescue_from ActiveRecode::ErrorClass do ~ end
みたいな書き方で、rescue_fromを定義したクラスの継承先で発生した例外をエラークラス毎にcatchする機能を持つ - 「継承先のクラスにしか適用されない」ので、
ApplicationController
やBaseController
に定義するのがベター- BaseController < ItemsController < ItemsSubController ... って感じで階層はどこまで行ってもcatchできる
- railsにデフォルトで定義される
ActiveRecode module
配下のエラークラスを定義できる(例えば...)- ActiveRecord::RecordNotFound
- 指定したidがtableで見つからなかった際に発生する
- find, find_byとか
- ActiveRecord::RecordInvalid
- バリデーションに失敗してrecpdeを保存できなかった際に発生する
- create!, update!とか
- ActiveRecord::RecordNotFound
実装
BaseController
class BaseController < ApplicationController
rescue_from ActiveRecord::RecordNotFound do
head :not_found
end
rescue_from ActiveRecord::RecordInvalid do
head :bad_request
end
end
ApplicationControllerを継承したBaseControllerでrescue_fromを定義しする。
今後BaseControllerを継承したクラスで発生する2種類の例外はすべてrescue_fromでcatchされます。
ItemsController
class Api::ArticlesController < BaseController
def update
article = Article.find(params[:id])
article.update!(article_params)
head :ok
end
end
検証用にBaseControllerを継承したItemsControlelrを作成し、update!
を実行しています。
ActiveRecord::RecordNotFound検証
endpointから存在しないidをわざと入れてRecpdeNotFound
エラーをrescue_fromが処理してくれるかを検証します。
既存リソース
{
"id": 1,
"title": "title01",
"content": "content01"
}
update実行
put http://localhost:3000/api/articles/100
body
{
"title": "title01edited",
"content": "content01"
}
idが100のリソースは存在しないので例外が発生します。
今回はエラークラス毎にheadを定義しているので、レスポンスは空でstatus codeとエラークラス名のみが返却されます
404 Not Found
ActiveRecord::RecordInvalid検証
titleにpresence: true
のvalidationをかけておいて、nullのリクエストで失敗するケースを検証します。
update実行
put http://localhost:3000/api/articles/1
body
{
"title": "",
"content": "content01"
}
titleがnullではバリデーションを通過できず、RecodeInvalid
が発生します。
400 Bad Request
こんな感じでControllerでの例外をBaseControllerのrescue_fromで定義した内容で処理できていますね。
最後に成功時の処理を見て機嫌を直してから終わります
update実行
put http://localhost:3000/api/articles/1
body
{
"title": "title01edited",
"content": "content01"
}
204 No Content
😊
Discussion