Railsでエラーページを表示する.
いつもpublic/404.htmlとか編集してる程度だったので、Railsの仕組みを使ったエラーページを作ろうかと色々試行錯誤してみた.
因にエラーページは通常はProductionモードでHTTPアクセスをローカルホスト( localhost / 127.0.0.1 )以外でアクセスしたときに確認できます.
エラーページの実装方法
方法はググってみると、ざっと3種類あった.
- public/404.htmlを書き換える
- ApplicationController内でrescue_fromを追記
- ApplicationController内でrescue_action_in_publicメソッドをオーバライド
色々やるなら、最後のrescue_action_in_publicオーバライドする方法が一番よさそう. ただ欲を言うとRails自身もProductionモードのときちゃんと、404.htmlとか表示してるからその機能っぽくしたい.
Railsのコードを読んでみた.
ActionController::RescueにDEFAULT_RESCUE_RESPONSESなんてモノがあるじゃないか!コレを使ってRailsは例外からHTTPステータスコードに変換して404.htmlを表示してた.
DEFAULT_RESCUE_RESPONSES = { 'ActionController::RoutingError' => :not_found, 'ActionController::UnknownAction' => :not_found, 'ActiveRecord::RecordNotFound' => :not_found, 'ActiveRecord::StaleObjectError' => :conflict, 'ActiveRecord::RecordInvalid' => :unprocessable_entity, 'ActiveRecord::RecordNotSaved' => :unprocessable_entity, 'ActionController::MethodNotAllowed' => :method_not_allowed, 'ActionController::NotImplemented' => :not_implemented, 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity }
ステータスコード自体はActionController::StatusCodes::STATUS_CODESで管理してた.
STATUS_CODES = { : : 200 => "OK", : 404 => "Not Found", : 500 => "Internal Server Error", : : }
例外毎にHTTPステータスを割り当てて表示する方法.
Railsのコードを読んで大体なにしてるか分かったので、404.htmlの用に例外からステータスコードを判定して表示するコードを書いてみた.
renderでレイアウトとか色々してるけどこの辺は普通のコントローラと書き方は同じ.
class ApplicationController < ActionController::Base # 例外とステータスの関連付け rescue_responses.update({ 'Foo::BarError' => :not_found }) protected # 確認をhttp://localhost 等で確認するときはコメントアウト # が定石 #def local_request? # false #end def rescue_action_in_public(exception) #:doc: status_message_sym = response_code_for_rescue(exception) || :internal_server_error @http_status_code = SYMBOL_TO_STATUS_CODE[status_message_sym] respond_to do |format| format.html { render :template => "commons/error.html", :layout => 'application', :status => @http_status_code } end end end
テンプレートをRAILS_ROOT/views/commons/error.html.erbに作成して、Productionモードで確認.
<% title = "#{@http_status_code} #{ActionController::StatusCodes::STATUS_CODES[@http_status_code]}" -%> <h1><%= title %></h1> <p> <% if @http_status_code == 403 # Forbidden -%> アクセス権限がありません. <% elsif @http_status_code == 404 # Not Found -%> お探しのページが見つかりませんでした. <% elsif @http_status_code == 422 # Unprocessable Entity -%> 指定されたエンティティの処理ができませんでした. <% elsif @http_status_code == 500 # Internal Server Error -%> システムに障害が発生しました. <% end -%> </p>