diff --git a/lib/grape/error_formatter/json.rb b/lib/grape/error_formatter/json.rb index 5359192340..f4df46e849 100644 --- a/lib/grape/error_formatter/json.rb +++ b/lib/grape/error_formatter/json.rb @@ -9,17 +9,18 @@ class << self def call(message, backtrace, options = {}, env = nil, original_exception = nil) result = wrap_message(present(message, env)) - rescue_options = options[:rescue_options] || {} - result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty? - result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception + result = merge_rescue_options(result, backtrace, options, original_exception) if result.is_a?(Hash) + ::Grape::Json.dump(result) end private def wrap_message(message) - if message.is_a?(Exceptions::ValidationErrors) || message.is_a?(Hash) + if message.is_a?(Hash) message + elsif message.is_a?(Exceptions::ValidationErrors) + message.as_json else { error: ensure_utf8(message) } end @@ -30,6 +31,14 @@ def ensure_utf8(message) message.encode('UTF-8', invalid: :replace, undef: :replace) end + + def merge_rescue_options(result, backtrace, options, original_exception) + rescue_options = options[:rescue_options] || {} + result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty? + result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception + + result + end end end end diff --git a/spec/grape/api_spec.rb b/spec/grape/api_spec.rb index 3ac2fe7454..f75f54c6cf 100644 --- a/spec/grape/api_spec.rb +++ b/spec/grape/api_spec.rb @@ -2743,6 +2743,57 @@ def self.call(message, _backtrace, _options, _env, _original_exception) it { is_expected.to have_key('backtrace') & have_key('original-exception') } end end + + context 'when rescue validation errors include backtrace and original exception' do + let(:app) do + response_type = response_format + + Class.new(Grape::API) do + format response_type + + rescue_from Grape::Exceptions::ValidationErrors, backtrace: true, original_exception: true do |e| + error!(e, 418, {}, e.backtrace, e) + end + + params do + requires :weather + end + get '/forecast' do + 'sunny' + end + end + end + + before do + get '/forecast' + end + + context 'with json response type format' do + subject { JSON.parse(last_response.body) } + + let(:response_format) { :json } + + it 'does not include backtrace or original exception' do + expect(subject).to match([{ 'messages' => ['is missing'], 'params' => ['weather'] }]) + end + end + + context 'with txt response type format' do + subject { last_response.body } + + let(:response_format) { :txt } + + it { is_expected.to include('backtrace', 'original exception') } + end + + context 'with xml response type format' do + subject { Grape::Xml.parse(last_response.body)['error'] } + + let(:response_format) { :xml } + + it { is_expected.to have_key('backtrace') & have_key('original-exception') } + end + end end describe '.content_type' do