Skip to content

Commit 32b8d22

Browse files
authored
Quality arguments to Accept header should default to 1.0 (#2366)
* Quality arguments to Accept header should default to 1.0 * space * Fix spec that results in XML * sort only once * Simplify * Ensure empty and invalid quality values also parse correctly, specs * Update CHANGELOG.md * Comments for readme and changelog
1 parent 8de048e commit 32b8d22

File tree

4 files changed

+40
-4
lines changed

4 files changed

+40
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
* [#2360](https://github.com/ruby-grape/grape/pull/2360): Reduce gem size by removing specs - [@ericproulx](https://github.com/ericproulx).
88
* [#2361](https://github.com/ruby-grape/grape/pull/2361): Remove `Rack::Auth::Digest` - [@ninoseki](https://github.com/ninoseki).
99
* Your contribution here.
10-
10+
1111
#### Fixes
1212

1313
* [#2364](https://github.com/ruby-grape/grape/pull/2364): Add missing requires - [@ericproulx](https://github.com/ericproulx).
14+
* [#2366](https://github.com/ruby-grape/grape/pull/2366): Default quality to 1.0 in the `Accept` header when omitted - [@hiddewie](https://github.com/hiddewie).
1415
* Your contribution here.
1516

1617
### 1.8.0 (2023/08/30)

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,10 @@ When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is ret
596596
option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route
597597
matches.
598598

599+
Grape will evaluate the relative quality preference included in Accept headers and default to a quality of 1.0 when omitted. In the following example a Grape API that supports XML and JSON in that order will return JSON:
600+
601+
curl -H "Accept: text/xml;q=0.8, application/json;q=0.9" localhost:1234/resource
602+
599603
### Accept-Version Header
600604

601605
```ruby
@@ -1600,7 +1604,7 @@ Note endless ranges are also supported with ActiveSupport >= 6.0, but they requi
16001604
```ruby
16011605
params do
16021606
requires :minimum, type: Integer, values: 10..
1603-
optional :maximum, type: Integer, values: ..10
1607+
optional :maximum, type: Integer, values: ..10
16041608
end
16051609
```
16061610

lib/grape/middleware/formatter.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,14 @@ def mime_array
164164
\w+/[\w+.-]+) # eg application/vnd.example.myformat+xml
165165
(?:
166166
(?:;[^,]*?)? # optionally multiple formats in a row
167-
;\s*q=([\d.]+) # optional "quality" preference (eg q=0.5)
167+
;\s*q=([\w.]+) # optional "quality" preference (eg q=0.5)
168168
)?
169169
}x
170170

171171
vendor_prefix_pattern = /vnd\.[^+]+\+/
172172

173173
accept.scan(accept_into_mime_and_quality)
174-
.sort_by { |_, quality_preference| -quality_preference.to_f }
174+
.sort_by { |_, quality_preference| -(quality_preference ? quality_preference.to_f : 1.0) }
175175
.flat_map { |mime, _| [mime, mime.sub(vendor_prefix_pattern, '')] }
176176
end
177177
end

spec/grape/middleware/formatter_spec.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,20 +125,51 @@ def to_xml
125125
it 'uses quality rankings to determine formats' do
126126
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json; q=0.3,application/xml; q=1.0')
127127
expect(subject.env['api.format']).to eq(:xml)
128+
128129
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json; q=1.0,application/xml; q=0.3')
129130
expect(subject.env['api.format']).to eq(:json)
130131
end
131132

132133
it 'handles quality rankings mixed with nothing' do
133134
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json,application/xml; q=1.0')
135+
expect(subject.env['api.format']).to eq(:json)
136+
137+
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/xml; q=1.0,application/json')
134138
expect(subject.env['api.format']).to eq(:xml)
135139
end
136140

141+
it 'handles quality rankings that have a default 1.0 value' do
142+
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json,application/xml;q=0.5')
143+
expect(subject.env['api.format']).to eq(:json)
144+
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/xml;q=0.5,application/json')
145+
expect(subject.env['api.format']).to eq(:json)
146+
end
147+
137148
it 'parses headers with other attributes' do
138149
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json; abc=2.3; q=1.0,application/xml; q=0.7')
139150
expect(subject.env['api.format']).to eq(:json)
140151
end
141152

153+
it 'ensures that a quality of 0 is less preferred than any other content type' do
154+
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json;q=0.0,application/xml')
155+
expect(subject.env['api.format']).to eq(:xml)
156+
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/xml,application/json;q=0.0')
157+
expect(subject.env['api.format']).to eq(:xml)
158+
end
159+
160+
it 'ignores invalid quality rankings' do
161+
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json;q=invalid,application/xml;q=0.5')
162+
expect(subject.env['api.format']).to eq(:xml)
163+
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/xml;q=0.5,application/json;q=invalid')
164+
expect(subject.env['api.format']).to eq(:xml)
165+
166+
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json;q=,application/xml;q=0.5')
167+
expect(subject.env['api.format']).to eq(:json)
168+
169+
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json;q=nil,application/xml;q=0.5')
170+
expect(subject.env['api.format']).to eq(:xml)
171+
end
172+
142173
it 'parses headers with vendor and api version' do
143174
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test-v1+xml')
144175
expect(subject.env['api.format']).to eq(:xml)

0 commit comments

Comments
 (0)