导轨4:404,500自定义错误页面和默认的500错误消息来自哪里?
目前在生产中我得到这个文本:
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或在这方面的任何东西。
在我的路线中,我有:
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
如何使它总是加载我的自定义错误? 在一些错误,它只是把这两行文本来自铁轨核心的某处,我希望它每次拾起我的自定义样式的错误页面! 怎么样? 谢谢!
您遇到的错误正在抛出
这意味着,你的例外被解救的代码本身就是抛出exception。 你可以检查你的日志文本:
Error during failsafe response:
找出exception来自哪里,从而解决您的问题。
新2017年
我们为此创build了一个gem – exception_handler
怎么运行的
所有Railsexception都通过config.exceptions_app
callback进行处理。 这是在config/application.rb
或config/environments/*.rb
文件中分配的 – 它需要是一个callback:
每当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
保存为ShowExceptions
顶部的ShowExceptions
。
自定义错误页面
如果你想创build自定义错误页面,你需要将自己的callback注入到config.exceptions_app
。 这可以在应用程序中完成,也可以使用gem来完成:
注意如何使用call
方法 – 这是callback的工作原理。 Rails( env
)在从Internet接收请求时被调用; 当引发exception时, env
被传递给exceptions_app
。
你的exception处理的质量将取决于你如何pipe理env
。 这个很重要; 引用self.routes
不会带来环境前进。
最好的方法是用一个单独的控制器来处理exception。 这使您可以像处理另一个视图一样处理请求,并授予对layout
和其他组件( model
/ email
)的访问权限。
–
处理exception有两种方法:
- 覆盖404/500路线
- 调用一个控制器
我们的gem是围绕我们的controller
devise的 – 每当引发exception
调用。 这完全控制了exception过程,允许100%的品牌布局 :
ExceptionHandler
现在是Rails的主要生产自定义错误页面gem。
保持3年以上,这是Rails最简单,最强大的exceptiongem。 它在Rails 5上运行率达到100%,已经下载了7万次以上。
gem
最新版本0.7.0
有以下更新:
- 自定义例外
- exception“映射”(select要处理的exception)
- 电子邮件通知
- 模型后端
- 链轮4+集成
你可以在这里阅读更多。
pipe理Rails的例外
如果你对gem不感兴趣,让我来解释一下这个过程:
所有Railsexception都通过config.exceptions_app
callback进行处理。 这是在config/application.rb
或config/environments/*.rb
文件中分配的 – 它需要是一个callback:
每当应用程序引发exception时, ShowExceptions
调用ShowExceptions
中间件。 该中间件将该exception构build到request
,并将其转发给config.exceptions_app
callback。
默认情况下, config.exceptions_app
指向路由。 这就是Rails在公用文件夹中带有404.html
, 500.html
和422.html
的原因。
如果你想创build自定义的exception页面,你需要重写config.exceptions_app
callback – 将错误的请求传递给适当的处理程序,无论是controller
还是route
:
[[中间件]]
有效pipe理这种方法的两种方法是将错误的请求发送给路由,或者调用控制器。
最简单也是最常用的方法是将请求转发给路由。 不幸的是,这忽略了请求,并阻止你正确地详细说明exception。
最好的方法是调用一个单独的控制器。 这将允许您传递整个请求,允许您保存,发送电子邮件或执行其他一些操作。
–
400/500错误
Rails 只能响应HTTP有效的错误 。
虽然应用程序的例外情况可能不同,但返回的状态代码应该是40x
或50x
。 这符合HTTP规范,并在这里概述↴
这意味着无论您使用/构build什么exception处理解决scheme,Rails都需要将40x
或50x
错误返回给浏览器。
换句话说,自定义错误页面与exceptiontypes无关 – 更多的是如何捕捉和提供浏览器响应 。
默认情况下,Rails使用422.html
404.html
, 422.html
和500.html
文件执行此操作。 如果您想自己处理exceptionstream程,则需要删除这些文件,并将错误的请求传递给您自己的exceptions_app
callback。
这可以通过routes
或controller
来完成(现在我将解释):
1.路线
最简单的方法是让路线处理它。
此方法臃肿,需要使用多个操作。 pipe理回应也很困难。
本教程解释:
这显示了如何直接replace路由的exceptions_app
:
# config/application.rb config.exceptions_app = self.routes
这里是我有的代码(Ruby 2.0.0,Rails 4.0):
应用程序configuration
#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>
虽然许多人更喜欢简单的“路线”方法,但它既不是有效的,也不是模块化的。 事实上,如果你的应用程序有任何面向对象的外观,你很快就会将其视为黑客。
一个更响亮的方法是使用自定义控制器来捕捉纯粹的exception。 这样,您可以根据您的应用程序的整体结构构buildstream程:
2.控制器
另一种select是将所有请求路由到一个控制器。
这是非常强大的,因为它允许您接受请求(例外)并将其传递给视图,同时在后端进行pipe理。 这将允许将其保存到数据库。
这个要点显示了:
这意味着我们可以挂钩到中间件并将整个请求传递给控制器。
如果这个控制器有一个模型和视图的支持,我们可以将它提取成一个gem(这就是我们所做的)。 如果您想手动执行此操作,请执行以下操作:
–
configuration
这种方法的config.exceptions_app
是它直接挂接到config.exceptions_app
。 这意味着任何exception都可以在本地处理,从而提高效率。 为了确保这个工作正常,你需要把下面的代码放到config/application.rb
( exceptions_app
只在production
– development
显示错误):
#config/application.rb config.exceptions_app = ->(env) { ExceptionController.action(:show).call(env) }
要testing,您可以将“本地”请求设置为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
–
查看
有两个视图来添加到这个工作。
第一个是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>
结论
exception与错误代码无关。
当Rails引发exception时,它会分配上述HTTP
响应代码之一。 这些允许您的浏览器确定请求是否成功。
在处理exception时,您需要确保能够处理40*
错误(通常与您的其他应用程序使用相同的布局)以及50*
错误(这将需要自己的布局)。
在这两种情况下,最好使用单独的exception
控制器,这将允许您将exception
作为对象进行pipe理。
应用程序中的错误页面应尽可能简单。 同样的build议涉及他们的渲染 如果您的应用程序返回500个HTTP响应代码,则意味着事情已经出错了。 而且有可能无法呈现错误页面并将其显示给用户。
理想情况下,错误页面应该是由您的Web服务器直接提供的纯HTML,而不会碰到应用程序服务器。
说到Rails实现这个想法。 它基于使用资产pipe道来预编译HTML静态页面。
首先添加新的资产types(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)
现在,您可以使用自己喜欢的模板引擎和Rails内置的视图助手在app / assets / html目录中创build漂亮的错误页面。
生产提示
生产资产pipe道将编辑资产的摘要添加到文件夹中,并将文件存储在默认文件夹下(通常为生产服务器上的共享/公共/资产)。 您可以使用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错误代码(示例nginxconfiguration):
error_page 500 502 503 504 /500.html; error_page 404 /404.html;
链轮3更新
对于Sprocket 3,你需要这样的东西(用Rails 5testing):
# 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')
这里是显示自定义404_error页面的最新和快速修复。
- 根据你的环境在development.rb或production.rb中添加以下行。
config.exceptions_app = self.routes
config.consider_all_requests_local = false
- 删除所有rm public / {404,500,422} .html
- 在您的rails项目的静态文件夹中创build404.html.erb文件。 你可以在这里添加自定义的html(这将使用你的应用程序布局,所以不要打扰页眉和页脚的内容)