rails 4:404,500的自定义错误页面以及来自哪里的默认500错误消息?

时间:2013-09-30 21:01:29

标签: error-handling ruby-on-rails-4

目前我正在制作本文:

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"

ErrorsController:

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

如何让它始终加载我的自定义错误?在一些错误上,它只是抛出来自rails core的2行文本,我希望它每次都能获取自定义样式的错误页面!怎么样? THX!

4 个答案:

答案 0 :(得分:70)

更新2018

我们的exception_handler gem现在被认为是最受欢迎的(Rails自定义错误页面)↴

enter image description here enter image description here


如何运作

使用config.exceptions_app处理所有Rails异常。这是在config/application.rbconfig/environments/*.rb文件中分配的 - 它需要是回调:

enter image description here

每当Rails遇到错误时,它都会调用ShowExceptions中间件。这会调用exception_app并将整个request(包括exception)发送到exceptions_app

Middleware-Powered Exceptions

exceptions_app 需要deliver a response。如果没有,则加载 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 ShowExceptions的顶部存储为FAILSAFE_RESPONSE


自定义错误页面

如果要创建自定义错误页面,则需要将自己的回调注入config.exceptions_app。这可以在应用程序中完成,也可以使用gem:

enter image description here

注意如何使用 call 方法 - 这就是回调的工作原理。从Internet接收请求时调用Rails(env);引发异常时,env将传递给exceptions_app

您的异常处理质量取决于您管理env的方式。这个很重要;引用self.routes使向前推进环境。

最好的方法是使用单独的控制器处理异常。这使您可以像处理另一个视图一样处理请求,授予对layout和其他组件(model / email)的访问权限。

-

两种方法来处理异常:

  1. 覆盖404 / 500路线
  2. 调用控制器
  3. 我们的gem是围绕controller设计的 - 每次引发exception时都会调用它。这使得完全控制异常流程,允许 100%品牌布局

    enter image description here

    ExceptionHandler现在是Rails的主要生产自定义错误页面宝石。

    维护超过3年,它是Rails最简单,最强大的异常宝石。它在Rails 5上100%工作,已经下载超过70,000次。


    宝石

    最新版本0.8.0.0有以下更新:

    • 自定义例外
    • 例外&#34;映射&#34; (选择要处理的例外情况)
    • 电子邮件通知
    • 模型后端
    • 链轮4+集成
    • RSpec Test Suite
    • 基于区域设置的视图

    您可以阅读更多here


    管理Rails&#39;例外

    如果您对宝石不感兴趣,请让我解释一下这个过程:

    使用config.exceptions_app回调处理所有Rails异常。这是在config/application.rbconfig/environments/*.rb文件中分配的 - 它需要是回调:

    enter image description here

    每当您的应用引发异常时,都会调用ShowExceptions中间件。此中间件将异常构建到request中,并将其转发给config.exceptions_app回调。

    默认情况下,config.exceptions_app指向路由。这就是Rails在404.html文件夹中附带500.html422.htmlpublic的原因。

    如果要创建自定义异常页面,则需要覆盖config.exceptions_app回调 - 将错误请求传递给适当的处理程序,无论是controller还是{ {1}}:

    [[中间件]]

    有效管理这两种方法的方法是将错误的请求发送到路由,或调用控制器。

    最简单 - 也是最常见 - 的方式是将请求转发到路由;遗憾的是,这会忽略该请求并阻止您正确地详细说明异常。

    最好的方法是调用一个单独的控制器。这将允许您传递整个请求,允许您保存,通过电子邮件发送或执行许多其他操作。

    -

    400/500错误

    Rails只能 以HTTP有效错误回复

    虽然应用的例外可能不同,但返回的状态代码route40x。这与HTTP规范一致,并概述了here

    enter image description here

    这意味着无论您使用/构建什么异常处理解决方案,Rails 都需要向浏览器返回50x40x错误。

    换句话说,自定义错误页面与异常的类型几乎没有关系 - 更多的是如何捕获和提供浏览器响应

    默认情况下,Rails使用50x文件夹中的404.html422.html500.html文件执行此操作。如果您想自己处理异常流程,则需要删除这些文件并将错误的请求传递给您自己的public回调。

    可以使用exceptions_approutes(我现在解释)完成此操作:


    1。路由

    最简单的方法是让路线处理它。

    此方法过时,需要使用多个操作。管理答复也很困难。

    This tutorial 解释:

    enter image description here

    这显示了如何直接用路由替换controller

    exceptions_app

    这是我的代码(Ruby 2.0.0,Rails 4.0):

    应用配置

    # config/application.rb
    config.exceptions_app = self.routes
    

    <强>路线

    #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>
    

    虽然许多人更喜欢&#34;路线&#34;方法简单,既不高效也不模块化。实际上,如果您的应用程序具有任何面向对象的外观,那么您很快就会将其视为黑客攻击。

    很多更响亮的方式是使用自定义控制器来捕获纯异常。这样,您就可以根据应用程序的整体结构构建流程:


    2。控制器

    另一种选择是将所有请求路由到控制器。

    这是无限强大的,因为它允许您接受请求(异常)并将其传递给视图,同时在后端进行管理。这样就可以将它保存到数据库中。

    gist显示了:

    enter image description here

    这意味着我们可以加入中间件&amp;将整个请求传递给控制器​​。

    如果此控制器由模型和视图支持,我们可以将其提取到gem(这就是我们所做的)。如果你想手动完成,请按照以下方式进行操作:

    -

    <强>配置

    这种方法的优点在于它直接挂钩到config.exceptions_app。这意味着可以原生处理任何异常,从而提高效率。为确保此方法有效,您需要将以下代码放入#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> config/application.rb仅适用于exceptions_app - production显示错误):

    development

    要进行测试,您可以设置&#34; local&#34;请求为false:

    #config/application.rb
    config.exceptions_app = ->(env) { ExceptionController.action(:show).call(env) }
    

    -

    <强>控制器

    下一步是添加#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 视图,第二个是exception/show。第一种是给layouts/error一个视图,第二种是exception_contoller#show内部服务器错误。

    500

    结论

    异常错误代码一样重要。

    当Rails引发异常时,它会分配上述#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> 响应代码之一。这些允许您的浏览器确定请求是否成功。

    在处理例外情况时,您需要确保能够处理HTTP错误(通常使用与应用程序其余部分相同的布局)和40*错误(这将需要他们自己的布局)。

    在这两种情况下,您最好使用单独的50*控制器,这样您就可以将exception作为对象进行管理。

答案 1 :(得分:43)

您正在遇到的错误正在从

中抛出

https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L18-L22

这意味着,您的异常获得的代码本身就会抛出异常。 您可以在日志中查看文字:

Error during failsafe response:

确定异常的真正原因,从而解决您的问题。

答案 2 :(得分:17)

应用程序中的错误页面应尽可能简单。相同的建议涉及他们的渲染如果您的应用程序返回500 HTTP响应代码,则意味着已经出现问题。并且您可能无法呈现错误页面并将其显示给用户。

理想情况下,错误页面应该是由Web服务器直接提供的纯HTML,而不会触及应用程序服务器。

说到Rails实现这个想法。它基于使用资产管道预编译HTML静态页面。

首先添加新资产类型(Rails&gt; 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)

现在您已准备好使用在app / assets / html目录中创建漂亮的错误页面 您最喜欢的模板引擎和Rails内置视图助手。

制作提示

在生产资产管道上,将摘要添加到已编译资产并将文件存储在默认文件夹下 (通常是生产服务器上的共享/公共/资产)。 您可以使用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

最后一件事。告诉Web服务器将这些文件用于某些HTTP错误代码 (示例nginx配置):

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

链轮3更新

对于Sprocket 3,你需要这样的东西(用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')

答案 3 :(得分:-3)

以下是显示自定义404_error页面的最新快速解决方法。

  • 根据 env development.rb production.rb 中添加以下行。
  

config.exceptions_app = self.routes

     

config.consider_all_requests_local = false

  • 删除所有rm public / {404,500,422} .html
  • 在rails项目的 static 文件夹中创建 404.html.erb 文件。你可以在这里添加你的自定义html。(这将使用你的应用程序布局,所以不要打扰页眉和页脚内容)