【Ruby on Rails】ActiveRecord::RecordNotFoundで落ちる画面を防ぐ方法

Ruby

こんにちは、かつコーチです。

存在しないIDのURLにアクセスしたら、真っ白なエラー画面が表示されて困った経験はありませんか。
本番環境でこの画面をユーザーに見せてしまうと、サービスへの信頼を損ねてしまいます。
この記事では、ActiveRecord::RecordNotFoundが発生する原因と、一元的に防ぐ方法を解説します。
読み終える頃には、404ページで丁寧に受け止められるアプリになります。

症状の再現:RecordNotFoundが発生する場面

存在しないIDへのアクセス

Railsのコントローラでfindメソッドを使っている場合、該当データがないとエラーが発生します。

# app/controllers/articles_controller.rb
def show
  @article = Article.find(params[:id])
end

削除済みの記事のURLをブックマークからアクセスした場合や、URLのIDを直接書き換えた場合に、このエラーは発生しやすくなります。

実装手順:find_byとrescue_fromによる対処

手順1:find_byとfindの違いを理解する

findは、指定したIDのデータが見つからない場合に例外を発生させます。
一方、find_byは該当データがなくても例外を起こさず、nilを返すだけです。

Article.find(999)     # => ActiveRecord::RecordNotFound が発生
Article.find_by(id: 999)  # => nil が返る

用途に応じて、この2つを使い分けることが基本方針になります。

手順2:find_byとnilチェックで個別に対処する

エラーを発生させたくない箇所では、find_bynilチェックを組み合わせます。

def show
  @article = Article.find_by(id: params[:id])
  if @article.nil?
    redirect_to articles_path, alert: "記事が見つかりませんでした"
    return
  end
end

この方法は柔軟ですが、コントローラの数だけ同じ処理を書くことになり、保守性が下がりやすいという欠点があります。

手順3:rescue_fromで一元的にハンドリングする

rescue_fromを使うと、アプリ全体で発生した特定の例外を1箇所でまとめて処理できます。

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, with: :render_not_found

  private

  def render_not_found
    render file: Rails.root.join("public/404.html"), status: :not_found, layout: false
  end
end

こう書くことで、どのコントローラでfindが失敗しても、統一された404画面を表示できるようになります。
個別のコントローラにnilチェックを書き続ける必要がなくなり、コードの見通しがよくなります。

よくあるつまずきポイント・エラー対処

Before/After:本番環境で例外がそのまま表示されてしまう

私が実際に経験したのは、開発環境では気づかず本番リリース後に発覚したケースです。

❌Before:rescue_fromを設定していない状態

# app/controllers/articles_controller.rb
def show
  @article = Article.find(params[:id])
end

削除済みの記事IDにアクセスされた際、本番環境のログには次のエラーが記録されていました。

ActiveRecord::RecordNotFound (Couldn't find Article with 'id'=42):
  app/controllers/articles_controller.rb:8:in `show'

開発環境ではデバッグ画面が表示されるため気づきにくいのですが、本番環境ではconfig.consider_all_requests_localfalseのため、素っ気ないエラーページが表示されていました。
Google Search Consoleでユーザーからの離脱率が高いページとして検出され、初めて問題に気づきました。

✅After:application_controller.rbでrescue_fromを設定する

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, with: :render_not_found

  private

  def render_not_found
    render "errors/not_found", status: :not_found
  end
end

専用の404ビューを用意し、「お探しのページは見つかりませんでした」という案内と、トップページへのリンクを表示するようにしました。
設定後は、削除済み記事へのアクセスでもユーザーが迷わず離脱できるようになりました。

まとめ

この記事のポイント

  • findは該当データがないと例外を発生させ、find_bynilを返す
  • 個別のコントローラでnilチェックする方法は柔軟だが、コードが重複しやすい
  • rescue_fromapplication_controller.rbに書けば、アプリ全体で一元的に処理できる
  • 本番環境で例外がそのまま表示されていないか、リリース前に必ず確認する

次に読むべき記事

エラー全般の対処法をさらに知りたい方は「Railsでよく出るエラーと解決法まとめ」も参考にしてください。
実際にfind_byの挙動を手元で確認したい方は「rails consoleを使いこなすデバッグ術」で試し方を解説しています。

タグ: Ruby on Rails, 中級者向け, エラー解決

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