Skip to content

Commit eb1711a

Browse files
Joe Fabernamusyaka
authored andcommitted
auto OPTIONS should not check params or call _validation hooks
Fixes #1506
1 parent aa81fff commit eb1711a

File tree

5 files changed

+96
-35
lines changed

5 files changed

+96
-35
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
Next Release
22
============
33

4+
#### Features
5+
46
* [#1503](https://github.com/ruby-grape/grape/pull/1503): Allow to use regexp validator with arrays - [@akoltun](https://github.com/akoltun).
57
* [#1507](https://github.com/ruby-grape/grape/pull/1507): Add group attributes for parameter definitions - [@304](https://github.com/304).
68
* [#1512](https://github.com/ruby-grape/grape/pull/1512): Fix for deeply nested params TypeError situation - [@krbs](https://github.com/krbs).
79
* Your contribution here.
810

11+
#### Fixes
12+
13+
* [#1505](https://github.com/ruby-grape/grape/pull/1505): Run `before` and `after` callbacks, but skip the rest when handling OPTIONS - [@jlfaber](https://github.com/jlfaber).
14+
915
0.18.0 (10/7/2016)
1016
==================
1117

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,7 +1807,9 @@ end
18071807

18081808
When you add a route for a resource, a route for the `OPTIONS`
18091809
method will also be added. The response to an OPTIONS request will
1810-
include an "Allow" header listing the supported methods.
1810+
include an "Allow" header listing the supported methods. If the resource
1811+
has `before` and `after` callbacks they will be executed, but no other callbacks will
1812+
run.
18111813

18121814
```ruby
18131815
class API < Grape::API
@@ -1837,7 +1839,9 @@ curl -v -X OPTIONS http://localhost:3000/rt_count
18371839
You can disable this behavior with `do_not_route_options!`.
18381840

18391841
If a request for a resource is made with an unsupported HTTP method, an
1840-
HTTP 405 (Method Not Allowed) response will be returned.
1842+
HTTP 405 (Method Not Allowed) response will be returned. If the resource
1843+
has `before` callbacks they will be executed, but no other callbacks will
1844+
run.
18411845

18421846
``` shell
18431847
curl -X DELETE -v http://localhost:3000/rt_count/
@@ -2797,6 +2801,14 @@ Before and after callbacks execute in the following order:
27972801

27982802
Steps 4, 5 and 6 only happen if validation succeeds.
27992803

2804+
If a request for a resource is made with an unsupported HTTP method (returning
2805+
HTTP 405) only `before` callbacks will be executed. The remaining callbacks will
2806+
be bypassed.
2807+
2808+
If a request for a resource is made that triggers the built-in `OPTIONS` handler,
2809+
only `before` and `after` callbacks will be executed. The remaining callbacks will
2810+
be bypassed.
2811+
28002812
#### Examples
28012813

28022814
Using a simple `before` block to set a header

UPGRADING.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
Upgrading Grape
22
===============
33

4+
### Upgrading to >= 0.18.1
5+
6+
#### Removed param processing from built-in OPTIONS handler
7+
8+
When a request is made to the built-in `OPTIONS` handler, only the `before` and `after`
9+
callbacks associated with the resource will be run. The `before_validation` and
10+
`after_validation` callbacks and parameter validations will be skipped.
11+
412
### Upgrading to >= 0.17.0
513

614
#### Removed official support for Ruby < 2.2.2

lib/grape/endpoint.rb

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ def method_name
124124
[options[:method],
125125
Namespace.joined_space(namespace_stackable(:namespace)),
126126
(namespace_stackable(:mount_path) || []).join('/'),
127-
options[:path].join('/')
128-
].join(' ')
127+
options[:path].join('/')]
128+
.join(' ')
129129
end
130130

131131
def routes
@@ -248,21 +248,18 @@ def run
248248

249249
run_filters befores, :before
250250

251-
allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS]
252-
raise Grape::Exceptions::MethodNotAllowed, header.merge('Allow' => allowed_methods) if !options? && allowed_methods
253-
254-
run_filters before_validations, :before_validation
255-
run_validators validations, request
256-
run_filters after_validations, :after_validation
251+
if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
252+
raise Grape::Exceptions::MethodNotAllowed, header.merge('Allow' => allowed_methods) unless options?
253+
header 'Allow', allowed_methods
254+
response_object = ''
255+
status 204
256+
else
257+
run_filters before_validations, :before_validation
258+
run_validators validations, request
259+
run_filters after_validations, :after_validation
260+
response_object = @block ? @block.call(self) : nil
261+
end
257262

258-
response_object =
259-
if options?
260-
header 'Allow', allowed_methods
261-
status 204
262-
''
263-
else
264-
@block ? @block.call(self) : nil
265-
end
266263
run_filters afters, :after
267264
cookies.write(header)
268265

spec/grape/api_spec.rb

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -516,9 +516,15 @@ class DummyFormatClass
516516
end
517517
send(verb, '/example')
518518
expect(last_response.body).to eql verb == 'head' ? '' : verb
519-
# Call it with a method other than the properly constrained one.
520-
send(used_verb = verbs[(verbs.index(verb) + 2) % verbs.size], '/example')
521-
expect(last_response.status).to eql used_verb == 'options' ? 204 : 405
519+
# Call it with all methods other than the properly constrained one.
520+
(verbs - [verb]).each do |other_verb|
521+
send(other_verb, '/example')
522+
expected_rc = if other_verb == 'options' then 204
523+
elsif other_verb == 'head' && verb == 'get' then 200
524+
else 405
525+
end
526+
expect(last_response.status).to eql expected_rc
527+
end
522528
end
523529
end
524530

@@ -549,6 +555,7 @@ class DummyFormatClass
549555
before_validation { raise 'before_validation filter should not run' }
550556
after_validation { raise 'after_validation filter should not run' }
551557
after { raise 'after filter should not run' }
558+
params { requires :only_for_get }
552559
get
553560
end
554561

@@ -573,6 +580,26 @@ class DummyFormatClass
573580
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
574581
end
575582

583+
it 'runs all filters and body with a custom OPTIONS method' do
584+
subject.namespace :example do
585+
before { header 'X-Custom-Header-1', 'foo' }
586+
before_validation { header 'X-Custom-Header-2', 'foo' }
587+
after_validation { header 'X-Custom-Header-3', 'foo' }
588+
after { header 'X-Custom-Header-4', 'foo' }
589+
options { 'yup' }
590+
get
591+
end
592+
593+
options '/example'
594+
expect(last_response.status).to eql 200
595+
expect(last_response.body).to eql 'yup'
596+
expect(last_response.headers['Allow']).to be_nil
597+
expect(last_response.headers['X-Custom-Header-1']).to eql 'foo'
598+
expect(last_response.headers['X-Custom-Header-2']).to eql 'foo'
599+
expect(last_response.headers['X-Custom-Header-3']).to eql 'foo'
600+
expect(last_response.headers['X-Custom-Header-4']).to eql 'foo'
601+
end
602+
576603
context 'when format is xml' do
577604
it 'returns a 405 for an unsupported method' do
578605
subject.format :xml
@@ -594,8 +621,8 @@ class DummyFormatClass
594621
context 'when accessing env' do
595622
it 'returns a 405 for an unsupported method' do
596623
subject.before do
597-
_custom_header_1 = headers['X-Custom-Header']
598-
_custom_header_2 = env['HTTP_X_CUSTOM_HEADER']
624+
_customheader1 = headers['X-Custom-Header']
625+
_customheader2 = env['HTTP_X_CUSTOM_HEADER']
599626
end
600627
subject.get 'example' do
601628
'example'
@@ -630,7 +657,11 @@ class DummyFormatClass
630657

631658
describe 'adds an OPTIONS route that' do
632659
before do
633-
subject.before { header 'X-Custom-Header', 'foo' }
660+
subject.before { header 'X-Custom-Header', 'foo' }
661+
subject.before_validation { header 'X-Custom-Header-2', 'bar' }
662+
subject.after_validation { header 'X-Custom-Header-3', 'baz' }
663+
subject.after { header 'X-Custom-Header-4', 'bing' }
664+
subject.params { requires :only_for_get }
634665
subject.get 'example' do
635666
'example'
636667
end
@@ -652,10 +683,22 @@ class DummyFormatClass
652683
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
653684
end
654685

655-
it 'has a X-Custom-Header' do
686+
it 'calls before hook' do
656687
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
657688
end
658689

690+
it 'does not call before_validation hook' do
691+
expect(last_response.headers.key?('X-Custom-Header-2')).to be false
692+
end
693+
694+
it 'does not call after_validation hook' do
695+
expect(last_response.headers.key?('X-Custom-Header-3')).to be false
696+
end
697+
698+
it 'calls after hook' do
699+
expect(last_response.headers['X-Custom-Header-4']).to eq 'bing'
700+
end
701+
659702
it 'has no Content-Type' do
660703
expect(last_response.content_type).to be_nil
661704
end
@@ -2555,13 +2598,11 @@ def static
25552598
params: {
25562599
'param1' => { required: true },
25572600
'param2' => { required: false }
2558-
}
2559-
},
2601+
} },
25602602
{ description: 'global description',
25612603
params: {
25622604
'param2' => { required: false }
2563-
}
2564-
}
2605+
} }
25652606
]
25662607
end
25672608
it 'merges the parameters of the namespace with the parameters of the method' do
@@ -2585,8 +2626,7 @@ def static
25852626
params: {
25862627
'ns_param' => { required: true, desc: 'namespace parameter' },
25872628
'method_param' => { required: false, desc: 'method parameter' }
2588-
}
2589-
}
2629+
} }
25902630
]
25912631
end
25922632
it 'merges the parameters of nested namespaces' do
@@ -2618,8 +2658,7 @@ def static
26182658
'ns1_param' => { required: true, desc: 'ns1 param' },
26192659
'ns2_param' => { required: true, desc: 'ns2 param' },
26202660
'method_param' => { required: false, desc: 'method param' }
2621-
}
2622-
}
2661+
} }
26232662
]
26242663
end
26252664
it 'groups nested params and prevents overwriting of params with same name in different groups' do
@@ -2662,8 +2701,7 @@ def static
26622701
'root_param' => { required: true, desc: 'root param' },
26632702
'nested' => { required: true, type: 'Array' },
26642703
'nested[nested_param]' => { required: true, desc: 'nested param' }
2665-
}
2666-
}
2704+
} }
26672705
]
26682706
end
26692707
it 'allows to set the type attribute on :group element' do

0 commit comments

Comments
 (0)