require'active_support/core_ext/exception'require'active_support/notifications'require'action_dispatch/http/request'moduleActionDispatch# This middleware rescues any exception returned by the application and renders# nice exception pages if it's being rescued locally.classShowExceptionsRESCUES_TEMPLATE_PATH=File.join(File.dirname(__FILE__),'templates')cattr_accessor:rescue_responses@@rescue_responses=Hash.new(:internal_server_error)@@rescue_responses.update({'ActionController::RoutingError'=>:not_found,'AbstractController::ActionNotFound'=>:not_found,'ActiveRecord::RecordNotFound'=>:not_found,'ActiveRecord::StaleObjectError'=>:conflict,'ActiveRecord::RecordInvalid'=>:unprocessable_entity,'ActiveRecord::RecordNotSaved'=>:unprocessable_entity,'ActionController::MethodNotAllowed'=>:method_not_allowed,'ActionController::NotImplemented'=>:not_implemented,'ActionController::InvalidAuthenticityToken'=>:unprocessable_entity})cattr_accessor:rescue_templates@@rescue_templates=Hash.new('diagnostics')@@rescue_templates.update({'ActionView::MissingTemplate'=>'missing_template','ActionController::RoutingError'=>'routing_error','AbstractController::ActionNotFound'=>'unknown_action','ActionView::Template::Error'=>'template_error'})FAILSAFE_RESPONSE=[500,{'Content-Type'=>'text/html'},["<html><body><h1>500 Internal Server Error</h1>"<<"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.</body></html>"]]definitialize(app,consider_all_requests_local=false)@app=app@consider_all_requests_local=consider_all_requests_localenddefcall(env)beginstatus,headers,body=@app.call(env)exception=nil# Only this middleware cares about RoutingError. So, let's just raise# it here.ifheaders['X-Cascade']=='pass'raiseActionController::RoutingError,"No route matches #{env['PATH_INFO'].inspect}"endrescueException=>exceptionraiseexceptionifenv['action_dispatch.show_exceptions']==falseendexception?render_exception(env,exception):[status,headers,body]endprivatedefrender_exception(env,exception)log_error(exception)request=Request.new(env)if@consider_all_requests_local||request.local?rescue_action_locally(request,exception)elserescue_action_in_public(exception)endrescueException=>failsafe_error$stderr.puts"Error during failsafe response: #{failsafe_error}\n#{failsafe_error.backtrace*"\n "}"FAILSAFE_RESPONSEend# Render detailed diagnostics for unhandled exceptions rescued from# a controller action.defrescue_action_locally(request,exception)template=ActionView::Base.new([RESCUES_TEMPLATE_PATH],:request=>request,:exception=>exception,:application_trace=>application_trace(exception),:framework_trace=>framework_trace(exception),:full_trace=>full_trace(exception))file="rescues/#{@@rescue_templates[exception.class.name]}.erb"body=template.render(:file=>file,:layout=>'rescues/layout.erb')render(status_code(exception),body)end# Attempts to render a static error page based on the# <tt>status_code</tt> thrown, or just return headers if no such file# exists. At first, it will try to render a localized static page.# For example, if a 500 error is being handled Rails and locale is :da,# it will first attempt to render the file at <tt>public/500.da.html</tt># then attempt to render <tt>public/500.html</tt>. If none of them exist,# the body of the response will be left empty.defrescue_action_in_public(exception)status=status_code(exception)locale_path="#{public_path}/#{status}.#{I18n.locale}.html"ifI18n.localepath="#{public_path}/#{status}.html"iflocale_path&&File.exist?(locale_path)render(status,File.read(locale_path))elsifFile.exist?(path)render(status,File.read(path))elserender(status,'')endenddefstatus_code(exception)Rack::Utils.status_code(@@rescue_responses[exception.class.name])enddefrender(status,body)[status,{'Content-Type'=>'text/html','Content-Length'=>body.bytesize.to_s},[body]]enddefpublic_pathdefined?(Rails.public_path)?Rails.public_path:'public_path'enddeflog_error(exception)returnunlessloggerActiveSupport::Deprecation.silencedomessage="\n#{exception.class} (#{exception.message}):\n"message<<exception.annoted_source_code.to_sifexception.respond_to?(:annoted_source_code)message<<" "<<application_trace(exception).join("\n ")logger.fatal("#{message}\n\n")endenddefapplication_trace(exception)clean_backtrace(exception,:silent)enddefframework_trace(exception)clean_backtrace(exception,:noise)enddeffull_trace(exception)clean_backtrace(exception,:all)enddefclean_backtrace(exception,*args)defined?(Rails)&&Rails.respond_to?(:backtrace_cleaner)?Rails.backtrace_cleaner.clean(exception.backtrace,*args):exception.backtraceenddefloggerdefined?(Rails.logger)?Rails.logger:Logger.new($stderr)endendend