私たちのexception_handler
gem は、Ruby on Rails のカスタム エラー ページに使用できます。
使い方
Ruby on Rails の例外はすべて で処理されconfig.exceptions_app
ます。これはconfig/application.rb
またはconfig/environments/*.rb
ファイルで割り当てられます - コールバックである必要があります:
config.exceptions_app は、例外が発生したときに ShowException ミドルウェアによって呼び出される例外アプリケーションを設定します。デフォルトは ActionDispatch::PublicExceptions.new(Rails.public_path) です。
Ruby on Rails でエラーが発生するたびに、ShowExceptions
ミドルウェアが呼び出されます。exception_app
これにより、全体request
( を含むexception
) が呼び出され、 に送信されexceptions_app
ます。
exceptions_app
応答を配信する必要があります。そうでない場合、failsafe
がロードされます。
# show_exceptions.rb#L38
def render_exception(env, exception)
wrapper = ExceptionWrapper.new(env, exception)
status = wrapper.status_code
env["action_dispatch.exception"] = wrapper.exception
env["PATH_INFO"] = "/#{status}"
response = @exceptions_app.call(request.env) # => exceptions_app callback
response[1]["X-Cascade"] == "pass" ? pass_response(status) : response
rescue Exception => failsafe_error # => raised if exceptions_app false
$stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
FAILSAFE_RESPONSE
end
の先頭にfailsafe
が格納されます。FAILSAFE_RESPONSE
ShowExceptions
カスタム エラー ページ
カスタム エラー ページを作成する場合は、独自のコールバックを に挿入する必要がありますconfig.exceptions_app
。これは、アプリケーションまたは gem で実行できます。
メソッドの使用方法に注意してくださいcall
。これがコールバックの仕組みです。Ruby on Rails ( env
) は、インターネットからリクエストを受け取ると呼び出されます。例外が発生すると、env
に渡されexceptions_app
ます。
例外処理の品質は、管理方法に依存しますenv
。これは重要; 参照は環境を進めself.routes
ません。
最良の方法は、別のコントローラーで例外を処理することです。これにより、リクエストを単なる別のビューであるかのように処理し、layout
およびその他のコンポーネント ( model
/ email
) へのアクセスを許可できます。
例外を処理するには、次の 2 つの方法があります。
- オーバーライド
404
/500
ルート
- コントローラーの呼び出し
私たちの gem はcontroller
、 an が発生するたびに呼び出される - を中心に設計されexception
ました。これにより、例外プロセスを完全に制御できるようになり、 100% ブランド化されたレイアウトが可能になります。Ruby on Rails 5 で 100% 動作します。
Ruby on Rails の例外の管理
gem に興味がない場合は、プロセスを説明します。
Ruby on Rails のすべての例外は、config.exceptions_app
コールバックで処理されます。これはconfig/application.rb
またはconfig/environments/*.rb
ファイルで割り当てられます - コールバックである必要があります:
config.exceptions_app は、例外が発生したときに ShowException ミドルウェアによって呼び出される例外アプリケーションを設定します。デフォルトは ActionDispatch::PublicExceptions.new(Rails.public_path) です。
アプリで例外が発生するたびに、ShowExceptions
ミドルウェアが呼び出されます。このミドルウェアは例外を に組み込み、それをコールバックrequest
に転送します。config.exceptions_app
デフォルトでconfig.exceptions_app
は、ルートを指します。これが、Rails が404.html
,500.html
とフォルダーに含ま422.html
れている理由です。public
カスタムの例外ページを作成する場合は、コールバックをオーバーライドする必要があります。エラーconfig.exceptions_app
のあるリクエストを適切なハンドラー ( acontroller
または) に渡しroute
ます。
【ミドルウェア】
これを効果的に管理する 2 つの方法は、誤った要求をルートに送信するか、コントローラーを呼び出すことです。
最も単純で最も一般的な方法は、リクエストをルートに転送することです。残念ながら、これはリクエストを無視し、例外を適切に詳述することを妨げます。
最良の方法は、別のコントローラーを呼び出すことです。これにより、リクエスト全体を渡すことができ、リクエストを保存したり、電子メールで送信したり、他の多くのことを実行したりできます。
400 / 500 エラー
Rails は、HTTP 有効なエラーでのみ応答できます。
アプリの例外は異なる場合がありますが、返されるステータス コードはまたは のいずれ40x
かになります50x
。これは HTTP 仕様に準拠しており、ここで概説されています。
これは、使用/構築する例外処理ソリューションに関係なく、Ruby on Railsはいずれかまたはエラーをブラウザーに返す必要があることを意味します。40x
50x
つまり、カスタム エラー ページは、例外の種類とはほとんど関係がなく、ブラウザの応答をキャッチして提供する方法とは関係がありません。
デフォルトでは、Ruby on Rails はフォルダー内の404.html
,422.html
および500.html
ファイルを使用してこれを行いpublic
ます。例外フローを自分で処理したい場合は、これらのファイルを削除して、誤った要求を独自のexceptions_app
コールバックに送る必要があります。
routes
これはまたは を使用して実行できますcontroller
(これについては後で説明します)。
1.ルート
最も簡単な方法は、ルートに処理させることです。
このメソッドは肥大化し、複数のアクションを使用する必要があります。また、レスポンスの管理も困難です。
exceptions_app
これは、 をルートに直接置き換える方法を示しています。
# config/application.rb
config.exceptions_app = self.routes
これが私が持っているコードです(Ruby 2.0.0およびRuby on Rails 4.0):
アプリケーション構成
#config/application.rb
config.exceptions_app = self.routes
ルート
#config/routes.rb
if Rails.env.production?
get '404', to: 'application#page_not_found'
get '422', to: 'application#server_error'
get '500', to: 'application#server_error'
end
アプリケーションコントローラー
#controllers/application_controller.rb
def page_not_found
respond_to do |format|
format.html { render template: 'errors/not_found_error', layout: 'layouts/application', status: 404 }
format.all { render nothing: true, status: 404 }
end
end
def server_error
respond_to do |format|
format.html { render template: 'errors/internal_server_error', layout: 'layouts/error', status: 500 }
format.all { render nothing: true, status: 500}
end
end
エラー レイアウト(完全に静的 -- サーバー エラーのみ)
#views/layouts/error.html.erb
<!DOCTYPE html>
<html>
<head>
<title><%= action_name.titleize %> :: <%= site_name %></title>
<%= csrf_meta_tags %>
<style>
body {
background: #fff;
font-family: Helvetica, Arial, Sans-Serif;
font-size: 14px;
}
.error_container {
display: block;
margin: auto;
margin: 10% auto 0 auto;
width: 40%;
}
.error_container .error {
display: block;
text-align: center;
}
.error_container .error img {
display: block;
margin: 0 auto 25px auto;
}
.error_container .message strong {
font-weight: bold;
color: #f00;
}
</style>
</head>
<body>
<div class="error_container">
<%= yield %>
</div>
</body>
</html>
エラー ビュー
#views/errors/not_found_error.html.erb
<div class="error">
<h2>Sorry, this page has moved, or doesn't exist!</h2>
</div>
#views/errors/internal_server_error.html.erb
<div class="error">
<div class="message">
<strong>Error!</strong>
We're sorry, but our server is experiencing problems :(
</div>
</div>
多くの人がその単純さから「ルート」方式を好みますが、効率的でもモジュール式でもありません。実際、アプリケーションがオブジェクト指向に似ている場合は、すぐにハッキングとして片付けてしまいます。
より確実な方法は、カスタム コントローラーを使用して純粋な例外をキャッチすることです。このようにして、アプリケーションの全体的な構造に従ってフローを構築できます。
2.コントローラー
もう 1 つのオプションは、すべての要求をコントローラーにルーティングすることです。
バックエンドで管理しながら、リクエスト (例外) を取得してビューに渡すことができるため、これは無限に強力です。これにより、データベースに保存することができます。
この要点はその方法を示しています。
これは、ミドルウェアにフックして、リクエスト全体をコントローラーに渡すことができることを意味します。
このコントローラーがモデルとビューに支えられている場合、それを gem に抽出できます (これが私たちが行ったことです)。手動で行う場合は、次の方法があります。
構成
このメソッドの優れた点は、 に直接フックされることconfig.exceptions_app
です。つまり、あらゆる例外をネイティブに処理できるため、効率が向上します。これが機能することを確認するには、次のコードをに配置する必要がありますconfig/application.rb
(exceptions_app
でのみ機能しますproduction
-development
エラーを表示します):
#config/application.rb
config.exceptions_app = ->(env) { ExceptionController.action(:show).call(env) }
テストするには、「ローカル」リクエストを false に設定します。
#config/environments/development.rb
config.consider_all_requests_local = false # true
コントローラ
exception
次のステップは、コントローラーを追加することです。これは で処理できますがapplication_controller
、独自に抽出する方がはるかに優れています。application.rb
--からの呼び出しに注意してくださいExceptionController.action(:show)
。
#app/controllers/exception_controller.rb
class ExceptionController < ApplicationController
#Response
respond_to :html, :xml, :json
#Dependencies
before_action :status
#Layout
layout :layout_status
####################
# Action #
####################
#Show
def show
respond_with status: @status
end
####################
# Dependencies #
####################
protected
#Info
def status
@exception = env['action_dispatch.exception']
@status = ActionDispatch::ExceptionWrapper.new(env, @exception).status_code
@response = ActionDispatch::ExceptionWrapper.rescue_responses[@exception.class.name]
end
#Format
def details
@details ||= {}.tap do |h|
I18n.with_options scope: [:exception, :show, @response], exception_name: @exception.class.name, exception_message: @exception.message do |i18n|
h[:name] = i18n.t "#{@exception.class.name.underscore}.title", default: i18n.t(:title, default: @exception.class.name)
h[:message] = i18n.t "#{@exception.class.name.underscore}.description", default: i18n.t(:description, default: @exception.message)
end
end
end
helper_method :details
####################
# Layout #
####################
private
#Layout
def layout_status
@status.to_s == "404" ? "application" : "error"
end
end
ビュー
これを機能させるために追加するビューが 2 つあります。
1 つ目はexception/show
ビューで、2 つ目はlayouts/error
です。exception_contoller#show
1 つ目はビューを提供するためのもので、2 つ目は500
内部サーバー エラーのためのものです。
#app/views/exception/show.html.erb
<h1><%= details[:name] %></h1>
<p><%= details[:message] %></p>
#app/views/layouts/error.html.erb (for 500 internal server errors)
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Error</title>
<style>
html {
height: 100%;
background: #fff;
}
body {
font-family: Helvetica, Arial, Sans-Serif;
font-size: 14px;
}
.error_container {
display: block;
margin: auto;
margin: 10% auto 0 auto;
width: 40%;
}
.error_container .error {
display: block;
text-align: center;
}
.error_container .error img {
display: block;
margin: 0 auto 15px auto;
}
.error_container .message > * {
display: block;
}
.error_container .message strong {
font-weight: bold;
color: #f00;
}
</style>
</head>
<body>
<div class="error_container"><%= yield %></div>
</body>
</html>
結論
例外は、エラー コードほど重要ではありません。
Ruby on Rails で例外が発生すると、上記の HTTP 応答コードのいずれかが割り当てられます。これらにより、ブラウザはリクエストが成功したかどうかを判断できます。
40*
例外を処理するときは、エラー (通常、アプリケーションの残りの部分と同じレイアウトを使用します) と50*
エラー (独自のレイアウトが必要です)を処理できることを確認する必要があります。
どちらの場合も、オブジェクトとしてexception
管理できる別のコントローラーを使用するのが最適です。exception