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

Railsのエラー画面は動的か静的か

Railsのエラー画面を静的なものにするか動的名ものにするかという話は、よく上がるかと思います。 Assetsを利用したいし、他言語化したいし、エラーコードでメッセージだけ出しわけしたいし、 というので動的エラー画面を選択することがあるかと思います。

ちょっと待って

結論から書くと、いろいろ調べるとできなくはないが、問題が発生しうるケースがあるので 可能であれば、静的で行く方が良いようです。
 
どうしてもという場合は、yuki24/rambulance を使いましょう。
 
そもそも503考慮するなら静的ページ用意しなくちゃならないですしね。

昔話

詳しく知りませんでしたが、3.2ぐらいからRailsの動的エラー画面の生成方法が変わっていたようです。 2.3時代は、rescure_action_in_public を利用していました。 RailsCasts懐かしい。

上記RailsCastsは、Pro(有料)版でrevicedされているようです。
#53 Handling Exceptions (revised) - RailsCasts

githubのソースを見ると、self.routes を突っ込むタイプのようでした。 この方法は、かつて Edge RailsGuides に載っていたらしいのですが(今は載ってない)、 問題があるようです(後述)。

config.exceptions_app = self.routes

動的エラーについて

ググると rescue_from 方式がたくさんヒットします。
確か rescue_from って問題あったんじゃなかったけっと、さらに調べると

yuki24 さんのコメントを見つけました。
qiitaではこの手の話題に対して、いろんなところでフォローしてくれているようでよく見かけました。
Railsの404,500エラーページをカスタマイズ - Qiita

ActionController::RoutingError は ApplicationController に
到達する前に投げられる例外なので、
rescue_from ActionController::RoutingError, ... は期待した通りに動かない
(get '*path', to: ... によって、動いているように見えているだけ)

なるほどそういうことなんですね。

ApplicationController で rescue_from Exception, ... 
すると Airbrake や Bugsnag なんかが動かなくなる。

これはよくわからないので調べます(後述)。

では、どうするのがよいかというと、Rails には config.exceptions_app という設定があり、
ここに Rack mountable なオブジェクトを与えることで動的なエラーページを生成可能になります。
とはいえ、エラーページのためだけにもうひとつ Rack アプリを作るのも面倒なので、
とりあえず ErrorsController というのを作って、lambda で囲って渡しちゃえば動きます。
config.exceptions_app = ->(env) { ErrorsController.action(:show).call(env) }

なるほど、そんなことになってたんですね。

もう少し調べてみる

以下が非常に参考になりました。
Rails の rescue_from で拾えない例外を exceptions_app で処理する - Qiita
rescue_from の問題点、exceptions_app でも完璧ではない点。

Railsアプリの例外ハンドリングとエラーページの表示についてまとめてみた - Qiita
ActionDispatch::PublicExceptions、NewRelic, Airbrake などは Rack Middleware に差し込むタイプのgemなので、使えなくなる。

Railsでエラーページを動的に - Qiita
config.exceptions_app = routes の問題点と rambulance について記載があります。
動的ページの問題を解決するために作成したとのこと。
でも、静的ページを用意するためのあくまでつなぎとして使って欲しいとのこと。