Skip to content

Commit 6d8b803

Browse files
committed
Merge pull request #906 from croeck/invalid-body-parse-error-not-rescued-by-handlers
Invalid body parse error not rescued by handlers
2 parents 4704cc5 + 3479ba0 commit 6d8b803

File tree

8 files changed

+131
-0
lines changed

8 files changed

+131
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Next Release
99
* [#901](https://github.com/intridea/grape/pull/901): Fix: callbacks defined in a version block are only called for the routes defined in that block - [@kushkella](https://github.com/kushkella).
1010
* [#886](https://github.com/intridea/grape/pull/886): Group of parameters made to require an explicit type of Hash or Array - [@jrichter1](https://github.com/jrichter1).
1111
* [#912](https://github.com/intridea/grape/pull/912): Extended the `:using` feature for param documentation to `optional` fields - [@croeck](https://github.com/croeck).
12+
* [#906](https://github.com/intridea/grape/pull/906): Fix: invalid body parse errors are not rescued by handlers - [@croeck](https://github.com/croeck).
1213
* Your contribution here.
1314

1415
0.10.1 (12/28/2014)

lib/grape.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ module Exceptions
5757
autoload :IncompatibleOptionValues, 'grape/exceptions/incompatible_option_values'
5858
autoload :MissingGroupTypeError, 'grape/exceptions/missing_group_type'
5959
autoload :UnsupportedGroupTypeError, 'grape/exceptions/unsupported_group_type'
60+
autoload :InvalidMessageBody, 'grape/exceptions/invalid_message_body'
6061
end
6162

6263
module ErrorFormatter
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# encoding: utf-8
2+
module Grape
3+
module Exceptions
4+
class InvalidMessageBody < Base
5+
def initialize(body_format)
6+
super(message: compose_message('invalid_message_body', body_format: body_format), status: 400)
7+
end
8+
end
9+
end
10+
end

lib/grape/locale/en.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,10 @@ en:
3535
all_or_none: 'provide all or none of parameters'
3636
missing_group_type: 'group type is required'
3737
unsupported_group_type: 'group type must be Array or Hash'
38+
invalid_message_body:
39+
problem: "message body does not match declared format"
40+
resolution:
41+
"when specifying %{body_format} as content-type, you must pass valid
42+
%{body_format} in the request's 'body'
43+
"
3844

lib/grape/middleware/formatter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ def read_rack_input(body)
8181
end
8282
env['rack.request.form_input'] = env['rack.input']
8383
end
84+
rescue Grape::Exceptions::Base => e
85+
raise e
8486
rescue StandardError => e
8587
throw :error, status: 400, message: e.message
8688
end

lib/grape/parser/json.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ module Json
44
class << self
55
def call(object, env)
66
MultiJson.load(object)
7+
rescue MultiJson::ParseError
8+
# handle JSON parsing errors via the rescue handlers or provide error message
9+
raise Grape::Exceptions::InvalidMessageBody, 'application/json'
710
end
811
end
912
end

lib/grape/parser/xml.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ module Xml
44
class << self
55
def call(object, env)
66
MultiXml.parse(object)
7+
rescue MultiXml::ParseError
8+
# handle XML parsing errors via the rescue handlers or provide error message
9+
raise Grape::Exceptions::InvalidMessageBody, 'application/xml'
710
end
811
end
912
end
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
require 'spec_helper'
2+
3+
describe Grape::Exceptions::ValidationErrors do
4+
context 'api with rescue_from :all handler' do
5+
subject { Class.new(Grape::API) }
6+
before {
7+
subject.rescue_from :all do |e|
8+
rack_response 'message was processed', 400
9+
end
10+
subject.params do
11+
requires :beer
12+
end
13+
subject.post '/beer' do
14+
'beer received'
15+
end
16+
}
17+
18+
def app
19+
subject
20+
end
21+
22+
context 'with content_type json' do
23+
it 'can recover from failed body parsing' do
24+
post '/beer', 'test', 'CONTENT_TYPE' => 'application/json'
25+
expect(last_response.status).to eq 400
26+
expect(last_response.body).to eq('message was processed')
27+
end
28+
end
29+
30+
context 'with content_type xml' do
31+
it 'can recover from failed body parsing' do
32+
post '/beer', 'test', 'CONTENT_TYPE' => 'application/xml'
33+
expect(last_response.status).to eq 400
34+
expect(last_response.body).to eq('message was processed')
35+
end
36+
end
37+
38+
context 'with content_type text' do
39+
it 'can recover from failed body parsing' do
40+
post '/beer', 'test', 'CONTENT_TYPE' => 'text/plain'
41+
expect(last_response.status).to eq 400
42+
expect(last_response.body).to eq('message was processed')
43+
end
44+
end
45+
46+
context 'with no specific content_type' do
47+
it 'can recover from failed body parsing' do
48+
post '/beer', 'test', {}
49+
expect(last_response.status).to eq 400
50+
expect(last_response.body).to eq('message was processed')
51+
end
52+
end
53+
end
54+
55+
context 'api without a rescue handler' do
56+
subject { Class.new(Grape::API) }
57+
before {
58+
subject.params do
59+
requires :beer
60+
end
61+
subject.post '/beer' do
62+
'beer received'
63+
end
64+
}
65+
66+
def app
67+
subject
68+
end
69+
70+
context 'and with content_type json' do
71+
it 'can recover from failed body parsing' do
72+
post '/beer', 'test', 'CONTENT_TYPE' => 'application/json'
73+
expect(last_response.status).to eq 400
74+
expect(last_response.body).to include('message body does not match declared format')
75+
expect(last_response.body).to include('application/json')
76+
end
77+
end
78+
79+
context 'with content_type xml' do
80+
it 'can recover from failed body parsing' do
81+
post '/beer', 'test', 'CONTENT_TYPE' => 'application/xml'
82+
expect(last_response.status).to eq 400
83+
expect(last_response.body).to include('message body does not match declared format')
84+
expect(last_response.body).to include('application/xml')
85+
end
86+
end
87+
88+
context 'with content_type text' do
89+
it 'can recover from failed body parsing' do
90+
post '/beer', 'test', 'CONTENT_TYPE' => 'text/plain'
91+
expect(last_response.status).to eq 400
92+
expect(last_response.body).to eq('beer is missing')
93+
end
94+
end
95+
96+
context 'and with no specific content_type' do
97+
it 'can recover from failed body parsing' do
98+
post '/beer', 'test', {}
99+
expect(last_response.status).to eq 400
100+
# plain response with text/html
101+
expect(last_response.body).to eq('beer is missing')
102+
end
103+
end
104+
end
105+
end

0 commit comments

Comments
 (0)