From ef40fb6fd88f2e3c3f989aef65e3ddddfadee814 Mon Sep 17 00:00:00 2001 From: Yuki Nishijima Date: Thu, 24 Jan 2019 18:48:39 -0500 Subject: Fixed a bug where the debug view does not show the error page properly There are two cases where the debug view does not show the error details properly: * When the cause is mapped to an HTTP status code the last exception is unexpectedly uwrapped * When the last error is thrown from a view template the debug view is not using the `rescues/template_error.html.erb` to generate the view Both the cases could be fixed by not unwrapping the exception. The only case where the exception should be unwrapped is when the last error is an `ActionView::Template::Error` object. In this case the HTTP status code is determined based on the cause. There are actually more wrapper exceptions that are intentionally thrown. However, there is a consistent pattern of setting the original message and original backtrace to the wrapper exception implemented, so the debug view will not lose the information about what went wrong eariler. --- .../middleware/exception_wrapper.rb | 24 +++++++++++++--------- .../action_dispatch/middleware/show_exceptions.rb | 2 +- actionpack/test/dispatch/debug_exceptions_test.rb | 21 +++++++++++++++++-- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index fb2b2bd3b0..1fb3e9db00 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -31,22 +31,34 @@ module ActionDispatch "ActionController::MissingExactTemplate" => "missing_exact_template", ) + cattr_accessor :wrapper_exceptions, default: [ + "ActionView::Template::Error" + ] + attr_reader :backtrace_cleaner, :exception, :wrapped_causes, :line_number, :file def initialize(backtrace_cleaner, exception) @backtrace_cleaner = backtrace_cleaner - @exception = original_exception(exception) + @exception = exception @wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner) expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError) end + def unwrapped_exception + if wrapper_exceptions.include?(exception.class.to_s) + exception.cause + else + exception + end + end + def rescue_template @@rescue_templates[@exception.class.name] end def status_code - self.class.status_code_for_exception(@exception.class.name) + self.class.status_code_for_exception(unwrapped_exception.class.name) end def application_trace @@ -122,14 +134,6 @@ module ActionDispatch Array(@exception.backtrace) end - def original_exception(exception) - if @@rescue_responses.has_key?(exception.cause.class.name) - exception.cause - else - exception - end - end - def causes_for(exception) return enum_for(__method__, exception) unless block_given? diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 3c88afd4d3..767143a368 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -45,7 +45,7 @@ module ActionDispatch backtrace_cleaner = request.get_header "action_dispatch.backtrace_cleaner" wrapper = ExceptionWrapper.new(backtrace_cleaner, exception) status = wrapper.status_code - request.set_header "action_dispatch.exception", wrapper.exception + request.set_header "action_dispatch.exception", wrapper.unwrapped_exception request.set_header "action_dispatch.original_path", request.path_info request.path_info = "/#{status}" response = @exceptions_app.call(request.env) diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 214e063276..abb42af53f 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -64,6 +64,12 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest rescue raise ActionView::Template::Error.new(template) end + when "/cause_mapped_to_rescue_responses" + begin + raise ActionController::ParameterMissing, :missing_param_key + rescue + raise NameError.new("uninitialized constant Userr") + end when "/missing_template" raise ActionView::MissingTemplate.new(%w(foo), "foo/index", %w(foo), false, "mailer") when "/bad_request" @@ -311,12 +317,22 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_match(""foo"=>"[FILTERED]"", body) end - test "show registered original exception for wrapped exceptions" do + test "show registered original exception if the last exception is TemplateError" do @app = DevelopmentApp get "/not_found_original_exception", headers: { "action_dispatch.show_exceptions" => true } assert_response 404 - assert_match(/AbstractController::ActionNotFound/, body) + assert_match %r{AbstractController::ActionNotFound}, body + assert_match %r{Showing /.*/actionpack/test/dispatch/debug_exceptions_test.rb}, body + end + + test "show the last exception and cause even when the cause is mapped to resque_responses" do + @app = DevelopmentApp + + get "/cause_mapped_to_rescue_responses", headers: { "action_dispatch.show_exceptions" => true } + assert_response 500 + assert_match %r{ActionController::ParameterMissing}, body + assert_match %r{NameError}, body end test "named urls missing keys raise 500 level error" do @@ -478,6 +494,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_select "#Application-Trace-0" do assert_select "code", /syntax error, unexpected/ end + assert_match %r{Showing /.*/actionpack/test/dispatch/debug_exceptions_test.rb}, body end test "debug exceptions app shows user code that caused the error in source view" do -- cgit v1.2.3