Yanonoblog!

こつこつと

Rails - 例外設計

はじめに

本記事では「パーフェクト Ruby on Rails」で学んだ内容と別途気になって調べた内容や知識も含めアウトプットしています。

書籍に記載されている内容を順序立ててまとめるというよりは、整理しておきたい周辺の知識と深ぼった内容を雑多に書いています。

例外設計

例外設計とは、プログラム中で発生する可能性がある例外(エラー)に対して、どのように対処するかを設計することです。

エラー処理を実装することで、エラーが発生した際には、原因を特定し、復旧することができます。

さらに、エラー処理を適切に実装している場合、エラーによる大きな問題やセキュリティリスクを回避することができます。

ソースコードも、エラー処理に関する部分が分かりやすくなり、保守性や可読性も向上するでしょう。

異常系のエラーを分類する

業務エラー

システムの設計や実装上の問題ではなく、業務設計の中で想定されているルールや手順に従わなかったことが原因で発生するエラーのことを指します。

  • 入力された値がバリデーションなど、設定したビジネスルールに違反していた場合
  • 期限切れや重複処理を行った場合
システムエラー

システムの設計や実装上の問題が原因で発生するエラーのことを指します。

  • データベースへのアクセスに失敗した場合
  • 画面、またはファイルなどの読み込みに失敗した場合
  • 保存したはずがされていない
  • 正常動作を行っているのに応答もなければエラー表記もない
  • 業務エラーは、操作ミスや入力ミスなど、ユーザー側に原因があるエラーのこと
  • システムエラーは、ユーザーがどうにもできないエラーのこと
  • システムエラーは、システムの運用者が責任を持って復旧させる必要がある
  • システムエラーのハンドリング

    システム自体の問題に起因するエラーが発生すると500エラー画面が表示されます。

    ※ 伊藤さんの記事も参考にさせていただいています。

    https://qiita.com/jnchito/items/3ef95ea144ed15df3637

    業務エラーの場合

    エラーの原因とユーザーが次に行うべき操作を画面に表示します。

    実装時の注意点
    • モデルのバリデーションを使う
      • エラー内容はモデルのerrorsに格納する。
    • コントローラーで業務エラーをハンドリングする
      • メソッド内で例外をraiseして、呼び出し側でrescueするのはNG。
      • メソッドの呼び出し側は戻り値を必ずチェックする。
    • フロントエンドの実装
      • 画面上にエラーメッセージを表示することで、ユーザーにエラーの原因を伝える

    システムエラーの場合

    開発時は以下のような原因を特定するための情報が含まれています

    • エラーが発生した場所や原因となったコードの行番号
    • エラーメッセージ
    • スタックトレース(エラーが発生した時のプログラムの呼び出し履歴)
    • リクエストパラメーターなどの情報

    実際のwebサービスではユーザーに分かりやすいエラー画面を作り込むことが望ましいです。

    実装時の注意点
    • Railsの共通処理に任せる
      • エラーログと500エラーの表示はRailsが自動的に行なってくれる
    • 破壊的メソッドを適切に扱う
      • 「普通に考えると業務エラーは起こりえない場合」や「万一エラーが起きたら処理を中止したい場合」は破壊的メソッド(save!やcreate!)を使う。
    途中でエラーが発生しても続行したいケース

    メールの一斉送信で、途中でエラーになるユーザーがいてもそこで処理を中止せず、残りのユーザーにメールを送信したいような場合はエラーをrescueして、処理を続行します。

    共通処理をメソッド化

    rescue内のコードは、以下のように共通処理としてメソッド化しておくと再利用しやすくなります。

    記事ではClassのサンプルで定義されていましたがmoduleで管理するイメージかなと思います。

    # app/controllers/concerns/error_handlers.rb
    
    module ErrorHandlers
      extend ActiveSupport::Concern
    
      included do
        rescue_from StandardError, with: :log_and_notify
      end
    
      def log_and_notify(e)
        Rails.logger.error e.class
        Rails.logger.error e.message
        Rails.logger.error e.backtrace.join("\n")
        Bugsnag.notify e
    
        # エラー画面を表示する場合は以下のように記述する
        # render 'errors/internal_server_error', status: :internal_server_error
      end
    end
    

    エラーハンドリングが必要なコントローラーで、include ErrorHandlersを記述することで、共通処理が適用されるようになります。

    class UsersController < ApplicationController
      include ErrorHandlers
    
      def index
        @users = User.all
      end
    
      def show
        @user = User.find(params[:id])
      end
    end
    

    参考

    続く…

    コメント

    本記事の内容は以上になります!

    書籍の続きのアウトプットも随時更新したいと思います。


    プログラミングスクールのご紹介 (卒業生より)

    お世話になったプログラミングスクールであるRUNTEQです♪

    https://runteq.jp/r/ohtFwbjW

    こちらのリンクを経由すると1万円引きになります。

    RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。

    もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。

    https://twitter.com/outputky