読者です 読者をやめる 読者になる 読者になる

【動的VS静的】Railsの404/500エラーページ 静的の勝利

揉めに揉める議論

最初、もりじゅんさんの以下の記事を参考にさせていただこうとしたのですが、

morizyun.github.io

Qiitaでも(多分もりじゅんさんの記事を参考にした)同じ例をみかけて、そこでyuki24さんが、この方法はあまりよい方法でないというコメントをしてました。

Railsの404,500エラーページをカスタマイズ - Qiita

このコメントでひと記事できてしまうやつ。。笑

必読です。。

ということでApplicationControllerに例外ハンドラを書いてそれに応じてrenderするtemplateを振り分ける、という方法はできれば避けたいですね。

そして、もともとRailsで用意されているpublic/404.htmlですが、404ページにナビゲーションバーやフッターを追加してその内容が変わるのであれば、あまり賢くないように思います。いちいち変更するのはとても面倒そうです。

そこでRailsconfig.exceptions_appという設定があり、それをうまく使うことでエラー処理を行うことが出来るのでそれを使おう、ということになります。

rambulanceを試してみる

この方法を用いている、yuki24さんが作られているgemを使いながらこの方法についてみていきたい。

github.com

まずはgemを追加してbundle install

# Gemfile
gem 'rambulance'
$ bundle install

そしたらranbulance:installでファイルのテンプレートを生成します。-eオプションで特定のテンプレートエンジンを指定できます。

$ rails g rambulance:install -e slim

Railsではproductionで動かしてもアドレスがlocalhost127.0.0.1だと本番用のエラーページを出してくれません。ranbulanceはその辺も対応してくださってます。素敵すぎる。。

以下の様なURLでエラーページにアクセスすることができます。

localhost:3000/rambulance/***

ここまで使い進めてやはりつまずいたのは、exceptions_appを用いているというところ。。

exceptions_appは3.2から追加された機能で、例外発生時に呼ばれる例外処理アプリケーションを設定するというもの。デフォルトではActionDispatch::PublicExceptions.new(Rails.public_path)が使用される。Release Noteからは以下。

Added config.exceptions_app to set the exceptions application invoked by the ShowException middleware when an exception happens. Defaults to ActionDispatch::PublicExceptions.new(Rails.public_path).

参考: Railsのエラーハンドリング - 一分一秒真剣勝負!

こいつでRails標準のActionDispatch::PublicExceptionsを独自のコントローラに差し替えることができます。

middlewareレベルでExceptionsをrescueするActionDispatch::PublicExceptionsクラスのコードは以下。読むととても勉強になります。

rails/show_exceptions.rb at f49d20ef36c2d339e7a988fdc52981cdb95af22f · rails/rails · GitHub

ですが、例外が発生した場合はApplicationControllerまで届かずActionDispach::PublicExceptionsにrescueされてそちらのcontrollerに処理が委託されるので、ApplicationControllerで定義しているインスタンスメソッドやincludeしているApplicationHelperなどのヘルパーメソッドは使えなくなってしまう。

僕の場合はspのviewのlayout切り替えをここで行っていたりstylesheetの読み込み用のメソッドを用意したりしていたので結構つらみがあった。

やっぱり動的にやるのきつい。。

yuki24さんもおっしゃるように、よほど動的にしないといけないケース以外では静的ページとしてエラーページを用意する方が今のところよさそうかなと思いました。。結局メンテナンスのときなど、HTTPサーバーのレイヤーからエラーを出さなければいけないときは別途対応が必要ですし。

この方の記事も読んでおくとRailsでのエラー処理の考えが深まります。。

kami30k.com

雑に学んだことをまとめると、ApplicationControllerで拾った例外でview振り分けるのでは(ActionController::RoutingError は ApplicationControllerに到達する前に例外が発生しているので)見た目の解決にしかなってないのでよくなくて、やるならActionDispatch::PublicExceptionsで委託するcontrollerを変えないといけないのだけど、それだとApplicationController経由しないから色々つらいよ、みたいな感じ。。。

yuki24さんの結論は「そこまでして動的にやる意味あるんですかね?」とのこと。。。w

参考: Railsでエラーページを動的に - Qiita

つらい

ということで結局静的ページでやることにしました。つらい