58

現在制作中です。次のテキストを取得しています。

500 Internal Server Error
If you are the administrator of this website, then please read this web application's
log file and/or the web server's log file to find out what went wrong.

そのページには HTML はありません。このコードはどこにありますか? public/500.htmlファイルがありません。

私のルートには次のものがあります。

  get "/404", :to => "errors#error_404"
  get "/422", :to => "errors#error_404"
  get "/500", :to => "errors#error_500"
  get "/505", :to => "errors#error_505"

エラーコントローラー:

class ErrorsController < ApplicationController

  def sub_layout
    "left"
  end

  def error_404
    render :status => 404, :formats => [:html], :layout => "white", :sub_layout => "left"
  end

  def error_422
    render :status => 422, :formats => [:html], :layout => "white", :sub_layout => "left"
  end

  def error_500
    render :status => 500, :formats => [:html], :layout => "white", :sub_layout => "left"
  end

  def error_505
    render :status => 505, :formats => [:html], :layout => "white", :sub_layout => "left"
  end

end

カスタムエラーを常にロードするにはどうすればよいですか? エラーによっては、Ruby on Rails コアからの 2 行のテキストがスローされるだけです。カスタムスタイルのエラーページを毎回ピックアップしたい!

4

3 に答える 3

64

私たちのexception_handlergem は、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_RESPONSEShowExceptions


カスタム エラー ページ

カスタム エラー ページを作成する場合は、独自のコールバックを に挿入する必要がありますconfig.exceptions_app。これは、アプリケーションまたは gem で実行できます。

コードのスクリーンショット

メソッドの使用方法に注意してくださいcall。これがコールバックの仕組みです。Ruby on Rails ( env) は、インターネットからリクエストを受け取ると呼び出されます。例外が発生すると、envに渡されexceptions_appます。

例外処理の品質は、管理方法に依存しますenv。これは重要; 参照は環境を進めself.routesません。

最良の方法は、別のコントローラーで例外を処理することです。これにより、リクエストを単なる別のビューであるかのように処理し、layoutおよびその他のコンポーネント ( model/ email) へのアクセスを許可できます。


例外を処理するには、次の 2 つの方法があります。

  1. オーバーライド404/500ルート
  2. コントローラーの呼び出し

私たちの 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はいずれかまたはエラーをブラウザーに返す必要があることを意味します。40x50x

つまり、カスタム エラー ページは、例外の種類とはほとんど関係がなく、ブラウザの応答をキャッチして提供する方法とは関係がありません。

デフォルトでは、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#show1 つ目はビューを提供するためのもので、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

于 2013-10-09T17:43:55.047 に答える
18

アプリケーションのエラー ページは、できるだけシンプルにする必要があります。同じ推奨事項は、レンダリングに関するものです。アプリケーションが 500 HTTP 応答コードを返す場合は、問題がすでに発生していることを意味します。また、エラー ページをレンダリングしてユーザーに表示できなかった可能性もあります。

理想的には、エラー ページは、アプリケーション サーバーにヒットすることなく、Web サーバーによって直接提供されるプレーンな HTML ページである必要があります。

Ruby on Railsの実装といえばこの考え方。これは、HTML 静的ページをプリコンパイルするためのアセット パイプラインの使用に基づいています。

まず、新しいアセット タイプを追加します (Ruby on Rails > 4.1):

# config/initializers/assets.rb

Rails.application.config.assets.precompile += %w(404.html 500.html)
Rails.application.config.assets.paths << Rails.root.join('app/assets/html')
Rails.application.config.assets.register_mime_type('text/html', '.html')

テンプレート エンジン ( Slim やHamlなど) を使用する場合は、初期化子を介して登録します。

# For Slim
Rails.application.assets.register_engine('.slim', Slim::Template)
# For Haml
Rails.application.assets.register_engine('.haml', Tilt::HamlTemplate)

これで、お気に入りのテンプレート エンジンと Ruby on Rails の組み込みビュー ヘルパーを使用して、app/assets/html ディレクトリにきれいなエラー ページを作成する準備が整いました。

制作のヒント

プロダクション アセット パイプラインでは、コンパイル済みアセットにダイジェストを追加し、ファイルをデフォルト フォルダ(通常、プロダクション サーバー上の shared/public/assets)に保存します。capistrano を使用して、エラー ページを Web サーバーのルートにコピーできます。

# config/deploy.rb
# Capistrano 3 only

namespace :deploy do
  desc 'Copy compiled error pages to public'
  task :copy_error_pages do
    on roles(:all) do
      %w(404 500).each do |page|
        page_glob = "#{current_path}/public/#{fetch(:assets_prefix)}/#{page}*.html"
        # copy newest asset
        asset_file = capture :ruby, %Q{-e "print Dir.glob('#{page_glob}').max_by { |file| File.mtime(file) }"}
        if asset_file
          execute :cp, "#{asset_file} #{current_path}/public/#{page}.html"
        else
          error "Error #{page} asset does not exist"
        end
      end
    end
  end
  after :finishing, :copy_error_pages
end

そして最後に。特定の HTTP エラー コードに対してこれらのファイルを使用するように Web サーバーに指示します (サンプル nginx 構成)。

error_page 500 502 503 504 /500.html;
error_page 404 /404.html;

スプロケット 3 アップデート

Sprocket 3 の場合、次のようなものが必要です (Ruby on Rails 5 でテスト済み):

# config/environments/production.rb
config.assets.configure do |env|
  env.register_transformer 'text/slim', 'text/html', Slim::Template
  env.register_mime_type 'text/slim', extensions: ['.html']
  env.register_engine '.slim', Slim::Template
end

# config/initializers/assets.rb
Rails.application.config.assets.precompile += %w(404.html 500.html)
Rails.application.config.assets.paths << Rails.root.join('app/assets/html')
于 2014-08-14T08:40:30.333 に答える