Skip to content

Commit 925a3fb

Browse files
committed
Merge pull request #1190 from tylerdooling/1162
Bypass formatting for statuses with no entity-body
2 parents d721c5d + e90dc3f commit 925a3fb

File tree

6 files changed

+79
-30
lines changed

6 files changed

+79
-30
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
* Your contribution here.
77

8+
* [#1190](https://github.com/ruby-grape/grape/putt/1190): Bypass formatting for statuses with no entity-body - [@tylerdooling](https://github.com/tylerdooling).
89
* [#1188](https://github.com/ruby-grape/grape/putt/1188): Allow parameters with more than one type - [@dslh](https://github.com/dslh).
910
* [#1179](https://github.com/ruby-grape/grape/pull/1179): Allow all RFC6838 valid characters in header vendor - [@suan](https://github.com/suan).
1011
* [#1170](https://github.com/ruby-grape/grape/pull/1170): Allow dashes and periods in header vendor - [@suan](https://github.com/suan).

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,6 +1976,11 @@ Built-in formatters are the following.
19761976
* `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`
19771977
* `:binary`: data will be returned "as is"
19781978

1979+
Response statuses that indicate no content as defined by [Rack](https://github.com/rack)
1980+
[here](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
1981+
will bypass serialization and the body entity - though there should be none -
1982+
will not be modified.
1983+
19791984
### JSONP
19801985

19811986
Grape supports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the

UPGRADING.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ Identical endpoints with different versions now work correctly. A regression int
1515

1616
See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
1717

18+
#### Bypasses formatters when status code indicates no content
19+
20+
To be consistent with rack and it's handling of standard responses
21+
associated with no content, both default and custom formatters will now
22+
be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
23+
24+
See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
25+
1826
### Upgrading to >= 0.12.0
1927

2028
#### Changes in middleware

lib/grape/middleware/formatter.rb

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,35 +21,36 @@ def before
2121
def after
2222
status, headers, bodies = *@app_response
2323

24-
if bodies.is_a?(Grape::Util::FileResponse)
25-
headers = ensure_content_type(headers)
26-
27-
response =
28-
Rack::Response.new([], status, headers) do |resp|
29-
resp.body = bodies.file
30-
end
24+
if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
25+
@app_response
3126
else
32-
# Allow content-type to be explicitly overwritten
33-
api_format = mime_types[headers[Grape::Http::Headers::CONTENT_TYPE]] || env[Grape::Env::API_FORMAT]
34-
formatter = Grape::Formatter::Base.formatter_for(api_format, options)
27+
build_formatted_response(status, headers, bodies)
28+
end
29+
end
3530

36-
begin
37-
bodymap = bodies.collect do |body|
38-
formatter.call(body, env)
39-
end
31+
private
4032

41-
headers = ensure_content_type(headers)
33+
def build_formatted_response(status, headers, bodies)
34+
headers = ensure_content_type(headers)
4235

43-
response = Rack::Response.new(bodymap, status, headers)
44-
rescue Grape::Exceptions::InvalidFormatter => e
45-
throw :error, status: 500, message: e.message
36+
if bodies.is_a?(Grape::Util::FileResponse)
37+
Rack::Response.new([], status, headers) do |resp|
38+
resp.body = bodies.file
4639
end
40+
else
41+
# Allow content-type to be explicitly overwritten
42+
formatter = fetch_formatter(headers, options)
43+
bodymap = bodies.collect { |body| formatter.call(body, env) }
44+
Rack::Response.new(bodymap, status, headers)
4745
end
48-
49-
response
46+
rescue Grape::Exceptions::InvalidFormatter => e
47+
throw :error, status: 500, message: e.message
5048
end
5149

52-
private
50+
def fetch_formatter(headers, options)
51+
api_format = mime_types[headers[Grape::Http::Headers::CONTENT_TYPE]] || env[Grape::Env::API_FORMAT]
52+
Grape::Formatter::Base.formatter_for(api_format, options)
53+
end
5354

5455
# Set the content type header for the API format if it is not already present.
5556
#

spec/grape/api_spec.rb

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -537,16 +537,38 @@ def subject.enable_root_route!
537537
expect(last_response.headers['Content-Type']).to eql 'text/plain'
538538
end
539539

540-
it 'adds an OPTIONS route that returns a 204, an Allow header and a X-Custom-Header' do
541-
subject.before { header 'X-Custom-Header', 'foo' }
542-
subject.get 'example' do
543-
'example'
540+
describe 'adds an OPTIONS route that' do
541+
before do
542+
subject.before { header 'X-Custom-Header', 'foo' }
543+
subject.get 'example' do
544+
'example'
545+
end
546+
options '/example'
547+
end
548+
549+
it 'returns a 204' do
550+
expect(last_response.status).to eql 204
551+
end
552+
553+
it 'has an empty body' do
554+
expect(last_response.body).to be_blank
555+
end
556+
557+
it 'has an Allow header' do
558+
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
559+
end
560+
561+
it 'has a X-Custom-Header' do
562+
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
563+
end
564+
565+
it 'has no Content-Type' do
566+
expect(last_response.content_type).to be_nil
567+
end
568+
569+
it 'has no Content-Length' do
570+
expect(last_response.content_length).to be_nil
544571
end
545-
options '/example'
546-
expect(last_response.status).to eql 204
547-
expect(last_response.body).to eql ''
548-
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
549-
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
550572
end
551573

552574
it 'allows HEAD on a GET request' do

spec/grape/middleware/formatter_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,18 @@ def to_xml
201201
end
202202
end
203203

204+
context 'no content responses' do
205+
let(:no_content_response) { ->(status) { [status, {}, ['']] } }
206+
207+
Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.each do |status|
208+
it "does not modify a #{status} response" do
209+
expected_response = no_content_response[status]
210+
allow(app).to receive(:call).and_return(expected_response)
211+
expect(subject.call({})).to eq(expected_response)
212+
end
213+
end
214+
end
215+
204216
context 'input' do
205217
%w(POST PATCH PUT DELETE).each do |method|
206218
['application/json', 'application/json; charset=utf-8'].each do |content_type|

0 commit comments

Comments
 (0)