Skip to content

Commit 3d1766a

Browse files
author
Michael Bleigh
committed
Merge pull request ruby-grape#37 from tedkulp/fix_params_in_post
Ability to handle incoming JSON in the body
2 parents 5d7e4a3 + 3def84a commit 3d1766a

File tree

3 files changed

+66
-17
lines changed

3 files changed

+66
-17
lines changed

lib/grape/middleware/base.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require 'multi_json'
2+
13
module Grape
24
module Middleware
35
class Base
@@ -53,11 +55,18 @@ module Formats
5355
:json => :encode_json,
5456
:txt => :encode_txt,
5557
}
58+
PARSERS = {
59+
:json => :decode_json
60+
}
5661

5762
def formatters
5863
FORMATTERS.merge(options[:formatters] || {})
5964
end
6065

66+
def parsers
67+
PARSERS.merge(options[:parsers] || {})
68+
end
69+
6170
def content_types
6271
CONTENT_TYPES.merge(options[:content_types] || {})
6372
end
@@ -82,6 +91,35 @@ def formatter_for(api_format)
8291
end
8392
end
8493

94+
def parser_for(api_format)
95+
spec = parsers[api_format]
96+
case spec
97+
when nil
98+
nil
99+
when Symbol
100+
method(spec)
101+
else
102+
spec
103+
end
104+
end
105+
106+
def decode_json(object)
107+
MultiJson.decode(object)
108+
end
109+
110+
def encode_json(object)
111+
if object.respond_to? :serializable_hash
112+
MultiJson.encode(object.serializable_hash)
113+
elsif object.respond_to? :to_json
114+
object.to_json
115+
else
116+
MultiJson.encode(object)
117+
end
118+
end
119+
120+
def encode_txt(object)
121+
object.respond_to?(:to_txt) ? object.to_txt : object.to_s
122+
end
85123
end
86124

87125
end

lib/grape/middleware/formatter.rb

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
require 'grape/middleware/base'
2-
require 'multi_json'
32

43
module Grape
54
module Middleware
@@ -10,7 +9,8 @@ def default_options
109
{
1110
:default_format => :txt,
1211
:formatters => {},
13-
:content_types => {}
12+
:content_types => {},
13+
:parsers => {}
1414
}
1515
end
1616

@@ -22,7 +22,20 @@ def before
2222
fmt = format_from_extension || format_from_header || options[:default_format]
2323

2424
if content_types.key?(fmt)
25-
env['api.format'] = fmt
25+
if !env['rack.input'].nil? and (body = env['rack.input'].read).strip.length != 0
26+
parser = parser_for fmt
27+
unless parser.nil?
28+
begin
29+
body = parser.call(body)
30+
env['rack.request.form_hash'] = !env['rack.request.form_hash'].nil? ? env['rack.request.form_hash'].merge(body) : body
31+
env['rack.request.form_input'] = env['rack.input']
32+
rescue
33+
# It's possible that it's just regular POST content -- just back off
34+
end
35+
end
36+
env['rack.input'].rewind
37+
end
38+
env['api.format'] = fmt
2639
else
2740
throw :error, :status => 406, :message => 'The requested format is not supported.'
2841
end
@@ -69,20 +82,6 @@ def after
6982
headers['Content-Type'] = content_types[env['api.format']]
7083
Rack::Response.new(bodymap, status, headers).to_a
7184
end
72-
73-
def encode_json(object)
74-
if object.respond_to? :serializable_hash
75-
MultiJson.encode(object.serializable_hash)
76-
elsif object.respond_to? :to_json
77-
object.to_json
78-
else
79-
MultiJson.encode(object)
80-
end
81-
end
82-
83-
def encode_txt(object)
84-
object.respond_to?(:to_txt) ? object.to_txt : object.to_s
85-
end
8685
end
8786
end
8887
end

spec/grape/middleware/formatter_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,16 @@ def serializable_hash
123123
body.body.should == ['CUSTOM JSON FORMAT']
124124
end
125125
end
126+
127+
context 'Input' do
128+
it 'should parse the body from a POST/PUT and put the contents into rack.request.form_hash' do
129+
subject.call({'PATH_INFO' => '/info', 'Accept' => 'application/json', 'rack.input' => StringIO.new('{"is_boolean":true,"string":"thing"}')})
130+
subject.env['rack.request.form_hash']['is_boolean'].should be_true
131+
subject.env['rack.request.form_hash']['string'].should == 'thing'
132+
end
133+
it 'should be able to fail gracefully if the body is regular POST content' do
134+
subject.call({'PATH_INFO' => '/info', 'Accept' => 'application/json', 'rack.input' => StringIO.new('name=Other+Test+Thing')})
135+
subject.env['rack.request.form_hash'].should be_nil
136+
end
137+
end
126138
end

0 commit comments

Comments
 (0)