aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/api_public_exceptions.rb47
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb41
3 files changed, 87 insertions, 2 deletions
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index dcd3ee0644..74ddf2954a 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -53,6 +53,7 @@ module ActionDispatch
autoload :ExceptionWrapper
autoload :Flash
autoload :ParamsParser
+ autoload :ApiPublicExceptions
autoload :PublicExceptions
autoload :Reloader
autoload :RemoteIp
diff --git a/actionpack/lib/action_dispatch/middleware/api_public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/api_public_exceptions.rb
new file mode 100644
index 0000000000..126842672d
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/api_public_exceptions.rb
@@ -0,0 +1,47 @@
+module ActionDispatch
+ class ApiPublicExceptions
+ attr_accessor :public_path
+
+ def initialize(public_path)
+ @public_path = public_path
+ end
+
+ def call(env)
+ exception = env["action_dispatch.exception"]
+ status = env["PATH_INFO"][1..-1]
+ request = ActionDispatch::Request.new(env)
+ content_type = request.formats.first
+ body = { :status => status, :error => exception.message }
+
+ render(status, content_type, body)
+ end
+
+ private
+
+ def render(status, content_type, body)
+ format = content_type && "to_#{content_type.to_sym}"
+ if format && body.respond_to?(format)
+ render_format(status, content_type, body.public_send(format))
+ else
+ render_html(status)
+ end
+ end
+
+ def render_format(status, content_type, body)
+ [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
+ 'Content-Length' => body.bytesize.to_s}, [body]]
+ end
+
+ def render_html(status)
+ found = false
+ path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
+ path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))
+
+ if found || File.exist?(path)
+ render_format(status, 'text/html', File.read(path))
+ else
+ [404, { "X-Cascade" => "pass" }, []]
+ end
+ end
+ end
+end
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index 72eaa916bc..c70bc5f95e 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -11,7 +11,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest
when "/bad_params"
raise ActionDispatch::ParamsParser::ParseError.new("", StandardError.new)
when "/method_not_allowed"
- raise ActionController::MethodNotAllowed
+ raise ActionController::MethodNotAllowed, 'PUT'
when "/unknown_http_method"
raise ActionController::UnknownHttpMethod
when "/not_found_original_exception"
@@ -22,7 +22,8 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest
end
end
- ProductionApp = ActionDispatch::ShowExceptions.new(Boomer.new, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public"))
+ ProductionApp = ActionDispatch::ShowExceptions.new(Boomer.new, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public"))
+ ProductionApiApp = ActionDispatch::ShowExceptions.new(Boomer.new, ActionDispatch::ApiPublicExceptions.new("#{FIXTURE_LOAD_PATH}/public"))
test "skip exceptions app if not showing exceptions" do
@app = ProductionApp
@@ -55,6 +56,42 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest
assert_equal "", body
end
+ test "rescue api apps with json response" do
+ @app = ProductionApiApp
+
+ get "/", headers: { 'HTTP_ACCEPT' => 'application/json', 'action_dispatch.show_exceptions' => true }
+ assert_response 500
+ assert_equal({ :status => '500', :error => 'puke!' }.to_json, body)
+
+ get "/method_not_allowed", headers: { 'HTTP_ACCEPT' => 'application/json', 'action_dispatch.show_exceptions' => true }
+ assert_response 405
+ assert_equal({ :status => '405', :error => 'Only PUT requests are allowed.' }.to_json, body)
+
+ get "/unknown_http_method", headers: { 'HTTP_ACCEPT' => 'application/json', 'action_dispatch.show_exceptions' => true }
+ assert_response 405
+ assert_equal({ :status => '405', :error => 'ActionController::UnknownHttpMethod' }.to_json, body)
+ end
+
+ test "rescue api apps unknown content-type requests with html response" do
+ @app = ProductionApiApp
+
+ get "/", headers: { 'HTTP_ACCEPT' => 'application/x-custom', 'action_dispatch.show_exceptions' => true }
+ assert_response 500
+ assert_equal "500 error fixture\n", body
+
+ get "/bad_params", headers: { 'HTTP_ACCEPT' => 'application/x-custom', 'action_dispatch.show_exceptions' => true }
+ assert_response 400
+ assert_equal "400 error fixture\n", body
+
+ get "/not_found", headers: { 'HTTP_ACCEPT' => 'application/x-custom', 'action_dispatch.show_exceptions' => true }
+ assert_response 404
+ assert_equal "404 error fixture\n", body
+
+ get "/unknown_http_method", headers: { 'HTTP_ACCEPT' => 'application/x-custom', 'action_dispatch.show_exceptions' => true }
+ assert_response 405
+ assert_equal("", body)
+ end
+
test "localize rescue error page" do
old_locale, I18n.locale = I18n.locale, :da