404、500 的自定义错误页面,但默认的 500 错误消息来自哪里?

2023-12-10

目前正在制作中,我收到以下文本:

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。该代码位于哪里?我没有公共/500.html file.

在我的路线中我有:

  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 核心某处的这两行文本。我希望它每次都能拾取我的自定义样式的错误页面!


Our exception_handlergem 可用于 Ruby on Rails 自定义错误页面。

怎么运行的

所有 Ruby on Rails 异常均通过以下方式处理config.exceptions_app。这被分配在config/application.rb or config/environments/*.rb文件 - 它需要是一个回调:

config.exceptions_app 设置异常发生时由 ShowException 中间件调用的异常应用程序。默认为 ActionDispatch::PublicExceptions.new(Rails.public_path)。

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

Middleware-Powered Exceptions

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

The failsafe存储为FAILSAFE_RESPONSE在顶端ShowExceptions.


自定义错误页面

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

Code screenshot

请注意如何call使用方法 - 这就是回调的工作原理。 Ruby on Rails (env)在从互联网收到请求时被调用;当异常发生时,env被传递给exceptions_app.

异常处理的质量取决于您的管理方式env。这个很重要;参考self.routes does not推动环境向前发展。

最好的方法是使用单独的控制器来处理异常。这允许您处理请求,就好像它只是另一个视图一样,授予对layout和其他组件(model / email).


two处理异常的方法:

  1. 覆盖404 / 500 routes
  2. 调用控制器

我们的宝石是围绕我们设计的controller- 每次调用exception被提出。这给出了full控制异常过程,允许100%品牌布局。它在 Ruby on Rails 5 上 100% 运行。


管理 Ruby on Rails 的异常

如果你对 gem 不感兴趣,让我解释一下这个过程:

所有 Ruby on Rails 异常均通过config.exceptions_app打回来。这被分配在config/application.rb or 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 and 422.html in the public folder.

如果你想创建custom异常页面,您需要覆盖config.exceptions_app回调 - 将错误的请求传递给适当的处理程序,无论是controller or route:

[中间件]

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

最简单也是最常见的方法是将请求转发到路由;不幸的是,这会忽略该请求并阻止您正确详细说明异常情况。

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


400 / 500 错误

导轨可以only使用 HTTP 有效错误进行响应.

虽然该应用程序的例外可能不同,返回的状态码should要么40x or 50x。这符合 HTTP 规范并概述here.

这意味着无论您使用/构建什么异常处理解决方案,Ruby on Railsneeds返回任一40x or 50x浏览器出现错误。

换句话说,自定义错误页面与type例外 - 更多如何捕捉和服务浏览器响应.

默认情况下,Ruby on Rails 使用以下命令执行此操作404.html, 422.html and 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

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>

虽然许多人更喜欢“路线”方法,因为它简单,但它既不高效也不模块化。事实上,如果您的应用程序具有任何面向对象的外观,您很快就会将其视为一种黑客行为。

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


2. 控制器

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

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

This gist显示如何。

这意味着我们可以挂接到中间件并传递整个request到控制器。

如果这个控制器由模型和视图支持,我们可以将其提取到 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

Views

要使其正常工作,需要添加两个视图。

第一个是exception/show视图,第二个是layouts/error。首先是给exception_contoller#show一个视图,第二个为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>

结论

The 例外并不那么重要错误代码.

当 Ruby on Rails 引发异常时,它会分配上述 HTTP 响应代码之一。这些允许您的浏览器确定请求是否成功。

处理异常时,您需要确保您能够处理40*错误(通常会使用与应用程序的其余部分相同的布局)和50*错误(需要自己的布局)。

在这两种情况下,您最好使用单独的exception控制器,这将允许您管理exception作为一个对象。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

404、500 的自定义错误页面,但默认的 500 错误消息来自哪里? 的相关文章

随机推荐