💬

【Rails】rescue_fromでエラークラスを一括でCatchする

2024/01/14に公開

rails apiで発生した例外をメソッド毎ではなく、定義と継承によって一括で処理するコードを解説します。

rescue_from

  • rescue_from ActiveRecode::ErrorClass do ~ endみたいな書き方で、rescue_fromを定義したクラスの継承先で発生した例外をエラークラス毎にcatchする機能を持つ
  • 「継承先のクラスにしか適用されない」ので、ApplicationControllerBaseControllerに定義するのがベター
    • BaseController < ItemsController < ItemsSubController ... って感じで階層はどこまで行ってもcatchできる
  • railsにデフォルトで定義されるActiveRecode module配下のエラークラスを定義できる(例えば...)
    • ActiveRecord::RecordNotFound
      • 指定したidがtableで見つからなかった際に発生する
      • find, find_byとか
    • ActiveRecord::RecordInvalid
      • バリデーションに失敗してrecpdeを保存できなかった際に発生する
      • create!, update!とか

実装

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