grape 1.8.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +65 -1
- data/README.md +377 -334
- data/UPGRADING.md +231 -6
- data/grape.gemspec +6 -10
- data/lib/grape/api/instance.rb +13 -10
- data/lib/grape/api.rb +17 -8
- data/lib/grape/content_types.rb +0 -2
- data/lib/grape/cookies.rb +2 -1
- data/lib/grape/dry_types.rb +0 -2
- data/lib/grape/dsl/desc.rb +23 -21
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +47 -22
- data/lib/grape/dsl/parameters.rb +4 -3
- data/lib/grape/dsl/routing.rb +20 -4
- data/lib/grape/dsl/validations.rb +13 -0
- data/lib/grape/endpoint.rb +15 -10
- data/lib/grape/{util/env.rb → env.rb} +0 -5
- data/lib/grape/error_formatter/txt.rb +11 -10
- data/lib/grape/exceptions/base.rb +3 -3
- data/lib/grape/exceptions/missing_group_type.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
- data/lib/grape/exceptions/validation.rb +0 -2
- data/lib/grape/exceptions/validation_array_errors.rb +1 -0
- data/lib/grape/exceptions/validation_errors.rb +1 -3
- data/lib/grape/extensions/hash.rb +5 -1
- data/lib/grape/http/headers.rb +18 -24
- data/lib/grape/{util/json.rb → json.rb} +1 -3
- data/lib/grape/locale/en.yml +3 -0
- data/lib/grape/middleware/auth/base.rb +0 -2
- data/lib/grape/middleware/auth/dsl.rb +0 -2
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/base.rb +0 -2
- data/lib/grape/middleware/error.rb +55 -50
- data/lib/grape/middleware/formatter.rb +21 -18
- data/lib/grape/middleware/globals.rb +1 -3
- data/lib/grape/middleware/stack.rb +2 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
- data/lib/grape/middleware/versioner/header.rb +17 -163
- data/lib/grape/middleware/versioner/param.rb +2 -4
- data/lib/grape/middleware/versioner/path.rb +1 -3
- data/lib/grape/namespace.rb +3 -4
- data/lib/grape/path.rb +24 -29
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +3 -5
- data/lib/grape/router/base_route.rb +39 -0
- data/lib/grape/router/greedy_route.rb +20 -0
- data/lib/grape/router/pattern.rb +39 -30
- data/lib/grape/router/route.rb +22 -59
- data/lib/grape/router.rb +30 -36
- data/lib/grape/util/accept/header.rb +19 -0
- data/lib/grape/util/accept_header_handler.rb +105 -0
- data/lib/grape/util/base_inheritable.rb +4 -4
- data/lib/grape/util/cache.rb +0 -3
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_values.rb +0 -2
- data/lib/grape/util/lazy/block.rb +29 -0
- data/lib/grape/util/lazy/object.rb +45 -0
- data/lib/grape/util/lazy/value.rb +38 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +34 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +70 -0
- data/lib/grape/util/reverse_stackable_values.rb +1 -6
- data/lib/grape/util/stackable_values.rb +1 -6
- data/lib/grape/util/strict_hash_configuration.rb +3 -3
- data/lib/grape/validations/attributes_doc.rb +38 -36
- data/lib/grape/validations/contract_scope.rb +71 -0
- data/lib/grape/validations/params_scope.rb +10 -9
- data/lib/grape/validations/types/array_coercer.rb +0 -2
- data/lib/grape/validations/types/build_coercer.rb +69 -71
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -11
- data/lib/grape/validations/types/json.rb +0 -2
- data/lib/grape/validations/types/primitive_coercer.rb +0 -2
- data/lib/grape/validations/types/set_coercer.rb +0 -3
- data/lib/grape/validations/types.rb +0 -3
- data/lib/grape/validations/validators/base.rb +2 -1
- data/lib/grape/validations/validators/default_validator.rb +5 -1
- data/lib/grape/validations/validators/length_validator.rb +42 -0
- data/lib/grape/validations/validators/values_validator.rb +8 -3
- data/lib/grape/validations.rb +3 -7
- data/lib/grape/version.rb +1 -1
- data/lib/grape/{util/xml.rb → xml.rb} +1 -1
- data/lib/grape.rb +38 -269
- metadata +33 -274
- data/lib/grape/eager_load.rb +0 -20
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
- data/lib/grape/router/attribute_translator.rb +0 -63
- data/lib/grape/util/lazy_block.rb +0 -27
- data/lib/grape/util/lazy_object.rb +0 -43
- data/lib/grape/util/lazy_value.rb +0 -91
- data/spec/grape/api/custom_validations_spec.rb +0 -213
- data/spec/grape/api/deeply_included_options_spec.rb +0 -56
- data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
- data/spec/grape/api/documentation_spec.rb +0 -59
- data/spec/grape/api/inherited_helpers_spec.rb +0 -114
- data/spec/grape/api/instance_spec.rb +0 -103
- data/spec/grape/api/invalid_format_spec.rb +0 -45
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
- data/spec/grape/api/nested_helpers_spec.rb +0 -50
- data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
- data/spec/grape/api/parameters_modification_spec.rb +0 -41
- data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
- data/spec/grape/api/recognize_path_spec.rb +0 -21
- data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
- data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
- data/spec/grape/api/shared_helpers_spec.rb +0 -36
- data/spec/grape/api_remount_spec.rb +0 -509
- data/spec/grape/api_spec.rb +0 -4356
- data/spec/grape/dsl/callbacks_spec.rb +0 -45
- data/spec/grape/dsl/desc_spec.rb +0 -98
- data/spec/grape/dsl/headers_spec.rb +0 -62
- data/spec/grape/dsl/helpers_spec.rb +0 -100
- data/spec/grape/dsl/inside_route_spec.rb +0 -531
- data/spec/grape/dsl/logger_spec.rb +0 -24
- data/spec/grape/dsl/middleware_spec.rb +0 -60
- data/spec/grape/dsl/parameters_spec.rb +0 -180
- data/spec/grape/dsl/request_response_spec.rb +0 -225
- data/spec/grape/dsl/routing_spec.rb +0 -275
- data/spec/grape/dsl/settings_spec.rb +0 -261
- data/spec/grape/dsl/validations_spec.rb +0 -55
- data/spec/grape/endpoint/declared_spec.rb +0 -846
- data/spec/grape/endpoint_spec.rb +0 -1085
- data/spec/grape/entity_spec.rb +0 -336
- data/spec/grape/exceptions/base_spec.rb +0 -81
- data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
- data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
- data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_option_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
- data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
- data/spec/grape/exceptions/validation_spec.rb +0 -19
- data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
- data/spec/grape/grape_spec.rb +0 -9
- data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
- data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
- data/spec/grape/integration/rack_spec.rb +0 -51
- data/spec/grape/loading_spec.rb +0 -44
- data/spec/grape/middleware/auth/base_spec.rb +0 -31
- data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
- data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
- data/spec/grape/middleware/base_spec.rb +0 -221
- data/spec/grape/middleware/error_spec.rb +0 -85
- data/spec/grape/middleware/exception_spec.rb +0 -294
- data/spec/grape/middleware/formatter_spec.rb +0 -461
- data/spec/grape/middleware/globals_spec.rb +0 -30
- data/spec/grape/middleware/stack_spec.rb +0 -155
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
- data/spec/grape/middleware/versioner/header_spec.rb +0 -345
- data/spec/grape/middleware/versioner/param_spec.rb +0 -171
- data/spec/grape/middleware/versioner/path_spec.rb +0 -62
- data/spec/grape/middleware/versioner_spec.rb +0 -21
- data/spec/grape/named_api_spec.rb +0 -19
- data/spec/grape/parser_spec.rb +0 -86
- data/spec/grape/path_spec.rb +0 -252
- data/spec/grape/presenters/presenter_spec.rb +0 -71
- data/spec/grape/request_spec.rb +0 -126
- data/spec/grape/util/inheritable_setting_spec.rb +0 -242
- data/spec/grape/util/inheritable_values_spec.rb +0 -79
- data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
- data/spec/grape/util/stackable_values_spec.rb +0 -128
- data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
- data/spec/grape/validations/attributes_doc_spec.rb +0 -153
- data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
- data/spec/grape/validations/params_scope_spec.rb +0 -1420
- data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
- data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
- data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
- data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
- data/spec/grape/validations/types_spec.rb +0 -111
- data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
- data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
- data/spec/grape/validations/validators/base_spec.rb +0 -38
- data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
- data/spec/grape/validations/validators/default_spec.rb +0 -463
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
- data/spec/grape/validations/validators/except_values_spec.rb +0 -192
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
- data/spec/grape/validations/validators/presence_spec.rb +0 -315
- data/spec/grape/validations/validators/regexp_spec.rb +0 -161
- data/spec/grape/validations/validators/same_as_spec.rb +0 -57
- data/spec/grape/validations/validators/values_spec.rb +0 -733
- data/spec/grape/validations/validators/zh-CN.yml +0 -10
- data/spec/grape/validations_spec.rb +0 -2030
- data/spec/integration/eager_load/eager_load_spec.rb +0 -15
- data/spec/integration/multi_json/json_spec.rb +0 -7
- data/spec/integration/multi_xml/xml_spec.rb +0 -7
- data/spec/shared/deprecated_class_examples.rb +0 -16
- data/spec/shared/versioning_examples.rb +0 -215
- data/spec/spec_helper.rb +0 -52
- data/spec/support/basic_auth_encode_helpers.rb +0 -11
- data/spec/support/chunks.rb +0 -14
- data/spec/support/content_type_helpers.rb +0 -15
- data/spec/support/endpoint_faker.rb +0 -25
- data/spec/support/file_streamer.rb +0 -13
- data/spec/support/integer_helpers.rb +0 -13
- data/spec/support/versioned_helpers.rb +0 -55
data/README.md
CHANGED
@@ -15,24 +15,22 @@
|
|
15
15
|
- [Grape for Enterprise](#grape-for-enterprise)
|
16
16
|
- [Installation](#installation)
|
17
17
|
- [Basic Usage](#basic-usage)
|
18
|
+
- [Rails 7.1](#rails-71)
|
18
19
|
- [Mounting](#mounting)
|
19
20
|
- [All](#all)
|
20
21
|
- [Rack](#rack)
|
21
|
-
- [ActiveRecord without Rails](#activerecord-without-rails)
|
22
|
-
- [Rails 4](#rails-4)
|
23
|
-
- [Rails 5+](#rails-5)
|
24
22
|
- [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
|
25
23
|
- [Rails](#rails)
|
26
|
-
- [
|
27
|
-
- [Rails 6.0](#rails-60)
|
24
|
+
- [Zeitwerk](#zeitwerk)
|
28
25
|
- [Modules](#modules)
|
29
26
|
- [Remounting](#remounting)
|
30
27
|
- [Mount Configuration](#mount-configuration)
|
31
28
|
- [Versioning](#versioning)
|
32
|
-
- [
|
33
|
-
|
34
|
-
|
35
|
-
|
29
|
+
- [Strategies](#strategies)
|
30
|
+
- [Path](#path)
|
31
|
+
- [Header](#header)
|
32
|
+
- [Accept-Version Header](#accept-version-header)
|
33
|
+
- [Param](#param)
|
36
34
|
- [Describing Methods](#describing-methods)
|
37
35
|
- [Configuration](#configuration)
|
38
36
|
- [Parameters](#parameters)
|
@@ -41,6 +39,7 @@
|
|
41
39
|
- [Include Parent Namespaces](#include-parent-namespaces)
|
42
40
|
- [Include Missing](#include-missing)
|
43
41
|
- [Evaluate Given](#evaluate-given)
|
42
|
+
- [Parameter Precedence](#parameter-precedence)
|
44
43
|
- [Parameter Validation and Coercion](#parameter-validation-and-coercion)
|
45
44
|
- [Supported Parameter Types](#supported-parameter-types)
|
46
45
|
- [Integer/Fixnum and Coercions](#integerfixnum-and-coercions)
|
@@ -57,6 +56,7 @@
|
|
57
56
|
- [values](#values)
|
58
57
|
- [except_values](#except_values)
|
59
58
|
- [same_as](#same_as)
|
59
|
+
- [length](#length)
|
60
60
|
- [regexp](#regexp)
|
61
61
|
- [mutually_exclusive](#mutually_exclusive)
|
62
62
|
- [exactly_one_of](#exactly_one_of)
|
@@ -70,6 +70,7 @@
|
|
70
70
|
- [Custom Validation messages](#custom-validation-messages)
|
71
71
|
- [presence, allow_blank, values, regexp](#presence-allow_blank-values-regexp)
|
72
72
|
- [same_as](#same_as-1)
|
73
|
+
- [length](#length-1)
|
73
74
|
- [all_or_none_of](#all_or_none_of-1)
|
74
75
|
- [mutually_exclusive](#mutually_exclusive-1)
|
75
76
|
- [exactly_one_of](#exactly_one_of-1)
|
@@ -79,6 +80,7 @@
|
|
79
80
|
- [Pass symbols for i18n translations](#pass-symbols-for-i18n-translations)
|
80
81
|
- [Overriding Attribute Names](#overriding-attribute-names)
|
81
82
|
- [With Default](#with-default)
|
83
|
+
- [Using dry-validation or dry-schema](#using-dry-validation-or-dry-schema)
|
82
84
|
- [Headers](#headers)
|
83
85
|
- [Request](#request)
|
84
86
|
- [Header Case Handling](#header-case-handling)
|
@@ -99,7 +101,6 @@
|
|
99
101
|
- [Rescuing exceptions inside namespaces](#rescuing-exceptions-inside-namespaces)
|
100
102
|
- [Unrescuable Exceptions](#unrescuable-exceptions)
|
101
103
|
- [Exceptions that should be rescued explicitly](#exceptions-that-should-be-rescued-explicitly)
|
102
|
-
- [Rails 3.x](#rails-3x)
|
103
104
|
- [Logging](#logging)
|
104
105
|
- [API Formats](#api-formats)
|
105
106
|
- [JSONP](#jsonp)
|
@@ -114,12 +115,13 @@
|
|
114
115
|
- [Active Model Serializers](#active-model-serializers)
|
115
116
|
- [Sending Raw or No Data](#sending-raw-or-no-data)
|
116
117
|
- [Authentication](#authentication)
|
117
|
-
- [Basic
|
118
|
+
- [Basic Auth](#basic-auth)
|
118
119
|
- [Register custom middleware for authentication](#register-custom-middleware-for-authentication)
|
119
120
|
- [Describing and Inspecting an API](#describing-and-inspecting-an-api)
|
120
121
|
- [Current Route and Endpoint](#current-route-and-endpoint)
|
121
122
|
- [Before, After and Finally](#before-after-and-finally)
|
122
123
|
- [Anchoring](#anchoring)
|
124
|
+
- [Instance Variables](#instance-variables)
|
123
125
|
- [Using Custom Middleware](#using-custom-middleware)
|
124
126
|
- [Grape Middleware](#grape-middleware)
|
125
127
|
- [Rails Middleware](#rails-middleware)
|
@@ -151,15 +153,11 @@
|
|
151
153
|
|
152
154
|
## What is Grape?
|
153
155
|
|
154
|
-
Grape is a REST-like API framework for Ruby. It's designed to run on Rack
|
155
|
-
or complement existing web application frameworks such as Rails and Sinatra by
|
156
|
-
providing a simple DSL to easily develop RESTful APIs. It has built-in support
|
157
|
-
for common conventions, including multiple formats, subdomain/prefix restriction,
|
158
|
-
content negotiation, versioning and much more.
|
156
|
+
Grape is a REST-like API framework for Ruby. It's designed to run on Rack or complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily develop RESTful APIs. It has built-in support for common conventions, including multiple formats, subdomain/prefix restriction, content negotiation, versioning and much more.
|
159
157
|
|
160
158
|
## Stable Release
|
161
159
|
|
162
|
-
You're reading the documentation for the stable release of Grape, **1.
|
160
|
+
You're reading the documentation for the stable release of Grape, **2.1.0**.
|
163
161
|
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
164
162
|
|
165
163
|
## Project Resources
|
@@ -177,7 +175,7 @@ The maintainers of Grape are working with Tidelift to deliver commercial support
|
|
177
175
|
|
178
176
|
## Installation
|
179
177
|
|
180
|
-
Ruby 2.
|
178
|
+
Ruby 2.7 or newer is required.
|
181
179
|
|
182
180
|
Grape is available as a gem, to install it run:
|
183
181
|
|
@@ -186,8 +184,7 @@ Grape is available as a gem, to install it run:
|
|
186
184
|
## Basic Usage
|
187
185
|
|
188
186
|
Grape APIs are Rack applications that are created by subclassing `Grape::API`.
|
189
|
-
Below is a simple example showing some of the more common features of Grape in
|
190
|
-
the context of recreating parts of the Twitter API.
|
187
|
+
Below is a simple example showing some of the more common features of Grape in the context of recreating parts of the Twitter API.
|
191
188
|
|
192
189
|
```ruby
|
193
190
|
module Twitter
|
@@ -266,6 +263,10 @@ module Twitter
|
|
266
263
|
end
|
267
264
|
```
|
268
265
|
|
266
|
+
## Rails 7.1
|
267
|
+
|
268
|
+
Grape's [deprecator](https://api.rubyonrails.org/v7.1.0/classes/ActiveSupport/Deprecation.html) will be added to your application's deprecators [automatically](lib/grape/railtie.rb) as `:grape`, so that your application's configuration can be applied to it.
|
269
|
+
|
269
270
|
## Mounting
|
270
271
|
|
271
272
|
### All
|
@@ -281,8 +282,7 @@ This can be added to your `config.ru` (if using rackup), `application.rb` (if us
|
|
281
282
|
|
282
283
|
### Rack
|
283
284
|
|
284
|
-
The above sample creates a Rack application that can be run from a rackup `config.ru` file
|
285
|
-
with `rackup`:
|
285
|
+
The above sample creates a Rack application that can be run from a rackup `config.ru` file with `rackup`:
|
286
286
|
|
287
287
|
```ruby
|
288
288
|
run Twitter::API
|
@@ -306,32 +306,9 @@ And would respond to the following routes:
|
|
306
306
|
|
307
307
|
Grape will also automatically respond to HEAD and OPTIONS for all GET, and just OPTIONS for all other routes.
|
308
308
|
|
309
|
-
### ActiveRecord without Rails
|
310
|
-
|
311
|
-
If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
|
312
|
-
is handled correctly.
|
313
|
-
|
314
|
-
#### Rails 4
|
315
|
-
|
316
|
-
The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
|
317
|
-
`config.ru` before mounting Grape, e.g.:
|
318
|
-
|
319
|
-
```ruby
|
320
|
-
use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
321
|
-
```
|
322
|
-
|
323
|
-
#### Rails 5+
|
324
|
-
|
325
|
-
Use [otr-activerecord](https://github.com/jhollinger/otr-activerecord) as follows:
|
326
|
-
|
327
|
-
```ruby
|
328
|
-
use OTR::ActiveRecord::ConnectionManagement
|
329
|
-
```
|
330
|
-
|
331
309
|
### Alongside Sinatra (or other frameworks)
|
332
310
|
|
333
|
-
If you wish to mount Grape alongside another Rack framework such as Sinatra, you can do so easily using
|
334
|
-
`Rack::Cascade`:
|
311
|
+
If you wish to mount Grape alongside another Rack framework such as Sinatra, you can do so easily using `Rack::Cascade`:
|
335
312
|
|
336
313
|
```ruby
|
337
314
|
# Example config.ru
|
@@ -367,21 +344,8 @@ Modify `config/routes`:
|
|
367
344
|
```ruby
|
368
345
|
mount Twitter::API => '/'
|
369
346
|
```
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
Modify `application.rb`:
|
374
|
-
|
375
|
-
```ruby
|
376
|
-
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
|
377
|
-
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
|
378
|
-
```
|
379
|
-
|
380
|
-
See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
|
381
|
-
|
382
|
-
#### Rails 6.0
|
383
|
-
|
384
|
-
For Rails versions greater than 6.0.0.beta2, `Zeitwerk` autoloader is the default for CRuby. By default `Zeitwerk` inflects `api` as `Api` instead of `API`. To make our example work, you need to uncomment the lines at the bottom of `config/initializers/inflections.rb`, and add `API` as an acronym:
|
347
|
+
#### Zeitwerk
|
348
|
+
Rails's default autoloader is `Zeitwerk`. By default, it inflects `api` as `Api` instead of `API`. To make our example work, you need to uncomment the lines at the bottom of `config/initializers/inflections.rb`, and add `API` as an acronym:
|
385
349
|
|
386
350
|
```ruby
|
387
351
|
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
@@ -391,8 +355,7 @@ end
|
|
391
355
|
|
392
356
|
### Modules
|
393
357
|
|
394
|
-
You can mount multiple API implementations inside another one. These don't have to be
|
395
|
-
different versions, but may be components of the same API.
|
358
|
+
You can mount multiple API implementations inside another one. These don't have to be different versions, but may be components of the same API.
|
396
359
|
|
397
360
|
```ruby
|
398
361
|
class Twitter::API < Grape::API
|
@@ -409,7 +372,7 @@ class Twitter::API < Grape::API
|
|
409
372
|
end
|
410
373
|
```
|
411
374
|
|
412
|
-
|
375
|
+
Declarations as `before/after/rescue_from` can be placed before or after `mount`. In any case they will be inherited.
|
413
376
|
|
414
377
|
```ruby
|
415
378
|
class Twitter::API < Grape::API
|
@@ -417,8 +380,20 @@ class Twitter::API < Grape::API
|
|
417
380
|
header 'X-Base-Header', 'will be defined for all APIs that are mounted below'
|
418
381
|
end
|
419
382
|
|
383
|
+
rescue_from :all do
|
384
|
+
error!({ "error" => "Internal Server Error" }, 500)
|
385
|
+
end
|
386
|
+
|
420
387
|
mount Twitter::Users
|
421
388
|
mount Twitter::Search
|
389
|
+
|
390
|
+
after do
|
391
|
+
clean_cache!
|
392
|
+
end
|
393
|
+
|
394
|
+
rescue_from ZeroDivisionError do
|
395
|
+
error!({ "error" => "Not found" }, 404)
|
396
|
+
end
|
422
397
|
end
|
423
398
|
```
|
424
399
|
|
@@ -549,10 +524,69 @@ end
|
|
549
524
|
|
550
525
|
## Versioning
|
551
526
|
|
552
|
-
|
553
|
-
|
527
|
+
You have the option to provide various versions of your API by establishing a separate `Grape::API` class for each offered version and then integrating them into a primary `Grape::API` class. Ensure that newer versions are mounted before older ones. The default approach to versioning directs the request to the subsequent Rack middleware if a specific version is not found.
|
528
|
+
|
529
|
+
```ruby
|
530
|
+
require 'v1'
|
531
|
+
require 'v2'
|
532
|
+
require 'v3'
|
533
|
+
class App < Grape::API
|
534
|
+
mount V3
|
535
|
+
mount V2
|
536
|
+
mount V1
|
537
|
+
end
|
538
|
+
```
|
554
539
|
|
555
|
-
|
540
|
+
To maintain the same endpoints from earlier API versions without rewriting them, you can indicate multiple versions within the previous API versions.
|
541
|
+
|
542
|
+
```ruby
|
543
|
+
class V1 < Grape::API
|
544
|
+
version 'v1', 'v2', 'v3'
|
545
|
+
|
546
|
+
get '/foo' do
|
547
|
+
# your code for GET /foo
|
548
|
+
end
|
549
|
+
|
550
|
+
get '/other' do
|
551
|
+
# your code for GET /other
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
class V2 < Grape::API
|
556
|
+
version 'v2', 'v3'
|
557
|
+
|
558
|
+
get '/var' do
|
559
|
+
# your code for GET /var
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
class V3 < Grape::API
|
564
|
+
version 'v3'
|
565
|
+
|
566
|
+
get '/foo' do
|
567
|
+
# your new code for GET /foo
|
568
|
+
end
|
569
|
+
end
|
570
|
+
```
|
571
|
+
|
572
|
+
Using the example provided, the subsequent endpoints will be accessible across various versions:
|
573
|
+
|
574
|
+
```shell
|
575
|
+
GET /v1/foo
|
576
|
+
GET /v1/other
|
577
|
+
GET /v2/foo # => Same behavior as v1
|
578
|
+
GET /v2/other # => Same behavior as v1
|
579
|
+
GET /v2/var # => New endpoint not available in v1
|
580
|
+
GET /v3/foo # => Different behavior to v1 and v2
|
581
|
+
GET /v3/other # => Same behavior as v1 and v2
|
582
|
+
GET /v3/var # => Same behavior as v2
|
583
|
+
```
|
584
|
+
|
585
|
+
There are four strategies in which clients can reach your API's endpoints: `:path`, `:header`, `:accept_version_header` and `:param`. The default strategy is `:path`.
|
586
|
+
|
587
|
+
### Strategies
|
588
|
+
|
589
|
+
#### Path
|
556
590
|
|
557
591
|
```ruby
|
558
592
|
version 'v1', using: :path
|
@@ -562,7 +596,7 @@ Using this versioning strategy, clients should pass the desired version in the U
|
|
562
596
|
|
563
597
|
curl http://localhost:9292/v1/statuses/public_timeline
|
564
598
|
|
565
|
-
|
599
|
+
#### Header
|
566
600
|
|
567
601
|
```ruby
|
568
602
|
version 'v1', using: :header, vendor: 'twitter'
|
@@ -580,16 +614,15 @@ Using this versioning strategy, clients should pass the desired version in the H
|
|
580
614
|
|
581
615
|
curl -H Accept:application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
|
582
616
|
|
583
|
-
By default, the first matching version is used when no `Accept` header is
|
584
|
-
supplied. This behavior is similar to routing in Rails. To circumvent this default behavior,
|
585
|
-
one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
|
586
|
-
is returned when no correct `Accept` header is supplied.
|
617
|
+
By default, the first matching version is used when no `Accept` header is supplied. This behavior is similar to routing in Rails. To circumvent this default behavior, one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error is returned when no correct `Accept` header is supplied.
|
587
618
|
|
588
|
-
When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is returned if the `:cascade`
|
589
|
-
option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route
|
590
|
-
matches.
|
619
|
+
When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is returned if the `:cascade` option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route matches.
|
591
620
|
|
592
|
-
|
621
|
+
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:
|
622
|
+
|
623
|
+
curl -H "Accept: text/xml;q=0.8, application/json;q=0.9" localhost:1234/resource
|
624
|
+
|
625
|
+
#### Accept-Version Header
|
593
626
|
|
594
627
|
```ruby
|
595
628
|
version 'v1', using: :accept_version_header
|
@@ -599,20 +632,15 @@ Using this versioning strategy, clients should pass the desired version in the H
|
|
599
632
|
|
600
633
|
curl -H "Accept-Version:v1" http://localhost:9292/statuses/public_timeline
|
601
634
|
|
602
|
-
By default, the first matching version is used when no `Accept-Version` header is
|
603
|
-
supplied. This behavior is similar to routing in Rails. To circumvent this default behavior,
|
604
|
-
one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
|
605
|
-
is returned when no correct `Accept` header is supplied and the `:cascade` option is set to `false`.
|
606
|
-
Otherwise a `404 Not Found` error is returned by Rack if no other route matches.
|
635
|
+
By default, the first matching version is used when no `Accept-Version` header is supplied. This behavior is similar to routing in Rails. To circumvent this default behavior, one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error is returned when no correct `Accept` header is supplied and the `:cascade` option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route matches.
|
607
636
|
|
608
|
-
|
637
|
+
#### Param
|
609
638
|
|
610
639
|
```ruby
|
611
640
|
version 'v1', using: :param
|
612
641
|
```
|
613
642
|
|
614
|
-
Using this versioning strategy, clients should pass the desired version as a request parameter,
|
615
|
-
either in the URL query string or in the request body.
|
643
|
+
Using this versioning strategy, clients should pass the desired version as a request parameter, either in the URL query string or in the request body.
|
616
644
|
|
617
645
|
curl http://localhost:9292/statuses/public_timeline?apiver=v1
|
618
646
|
|
@@ -703,13 +731,11 @@ API.configure do |config|
|
|
703
731
|
end
|
704
732
|
```
|
705
733
|
|
706
|
-
This will be available inside the API with `configuration`, as if it were
|
707
|
-
[mount configuration](#mount-configuration).
|
734
|
+
This will be available inside the API with `configuration`, as if it were [mount configuration](#mount-configuration).
|
708
735
|
|
709
736
|
## Parameters
|
710
737
|
|
711
|
-
Request parameters are available through the `params` hash object. This includes `GET`, `POST`
|
712
|
-
and `PUT` parameters, along with any named parameters you specify in your route strings.
|
738
|
+
Request parameters are available through the `params` hash object. This includes `GET`, `POST` and `PUT` parameters, along with any named parameters you specify in your route strings.
|
713
739
|
|
714
740
|
```ruby
|
715
741
|
get :public_timeline do
|
@@ -717,8 +743,7 @@ get :public_timeline do
|
|
717
743
|
end
|
718
744
|
```
|
719
745
|
|
720
|
-
Parameters are automatically populated from the request body on `POST` and `PUT` for form input, JSON and
|
721
|
-
XML content-types.
|
746
|
+
Parameters are automatically populated from the request body on `POST` and `PUT` for form input, JSON and XML content-types.
|
722
747
|
|
723
748
|
The request:
|
724
749
|
|
@@ -1057,8 +1082,7 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
|
|
1057
1082
|
}
|
1058
1083
|
````
|
1059
1084
|
|
1060
|
-
Note that an attribute with a `nil` value is not considered *missing* and will also be returned
|
1061
|
-
when `include_missing` is set to `false`:
|
1085
|
+
Note that an attribute with a `nil` value is not considered *missing* and will also be returned when `include_missing` is set to `false`:
|
1062
1086
|
|
1063
1087
|
**Request**
|
1064
1088
|
|
@@ -1176,6 +1200,35 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/child -d '{"chil
|
|
1176
1200
|
}
|
1177
1201
|
````
|
1178
1202
|
|
1203
|
+
### Parameter Precedence
|
1204
|
+
|
1205
|
+
Using `route_param` takes higher precedence over a regular parameter defined with same name:
|
1206
|
+
|
1207
|
+
```ruby
|
1208
|
+
params do
|
1209
|
+
requires :foo, type: String
|
1210
|
+
end
|
1211
|
+
route_param :foo do
|
1212
|
+
get do
|
1213
|
+
{ value: params[:foo] }
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
```
|
1217
|
+
|
1218
|
+
**Request**
|
1219
|
+
|
1220
|
+
```bash
|
1221
|
+
curl -X POST -H "Content-Type: application/json" localhost:9292/bar -d '{"foo": "baz"}'
|
1222
|
+
```
|
1223
|
+
|
1224
|
+
**Response**
|
1225
|
+
|
1226
|
+
```json
|
1227
|
+
{
|
1228
|
+
"value": "bar"
|
1229
|
+
}
|
1230
|
+
```
|
1231
|
+
|
1179
1232
|
## Parameter Validation and Coercion
|
1180
1233
|
|
1181
1234
|
You can define validations and coercion options for your parameters using a `params` block.
|
@@ -1197,8 +1250,7 @@ put ':id' do
|
|
1197
1250
|
end
|
1198
1251
|
```
|
1199
1252
|
|
1200
|
-
When a type is specified an implicit validation is done after the coercion to ensure
|
1201
|
-
the output type is the one declared.
|
1253
|
+
When a type is specified an implicit validation is done after the coercion to ensure the output type is the one declared.
|
1202
1254
|
|
1203
1255
|
Optional parameters can have a default value.
|
1204
1256
|
|
@@ -1210,9 +1262,7 @@ params do
|
|
1210
1262
|
end
|
1211
1263
|
```
|
1212
1264
|
|
1213
|
-
Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
|
1214
|
-
number for each call to the endpoint of this `params` block. To have the default evaluate
|
1215
|
-
lazily with each request use a lambda, like `:random_number` above.
|
1265
|
+
Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same number for each call to the endpoint of this `params` block. To have the default evaluate lazily with each request use a lambda, like `:random_number` above.
|
1216
1266
|
|
1217
1267
|
Note that default values will be passed through to any validation options specified.
|
1218
1268
|
The following example will always fail if `:color` is not explicitly provided.
|
@@ -1231,6 +1281,15 @@ params do
|
|
1231
1281
|
end
|
1232
1282
|
```
|
1233
1283
|
|
1284
|
+
You can use the value of one parameter as the default value of some other parameter. In this case, if the `primary_color` parameter is not provided, it will have the same value as the `color` one. If both of them not provided, both of them will have `blue` value.
|
1285
|
+
|
1286
|
+
```ruby
|
1287
|
+
params do
|
1288
|
+
optional :color, type: String, default: 'blue'
|
1289
|
+
optional :primary_color, type: String, default: -> (params) { params[:color] }
|
1290
|
+
end
|
1291
|
+
```
|
1292
|
+
|
1234
1293
|
### Supported Parameter Types
|
1235
1294
|
|
1236
1295
|
The following are all valid types, supported out of the box by Grape:
|
@@ -1272,12 +1331,7 @@ get '/int' integers: { int: '45' }
|
|
1272
1331
|
|
1273
1332
|
### Custom Types and Coercions
|
1274
1333
|
|
1275
|
-
Aside from the default set of supported types listed above, any class can be
|
1276
|
-
used as a type as long as an explicit coercion method is supplied. If the type
|
1277
|
-
implements a class-level `parse` method, Grape will use it automatically.
|
1278
|
-
This method must take one string argument and return an instance of the correct
|
1279
|
-
type, or return an instance of `Grape::Types::InvalidValue` which optionally
|
1280
|
-
accepts a message to be returned in the response.
|
1334
|
+
Aside from the default set of supported types listed above, any class can be used as a type as long as an explicit coercion method is supplied. If the type implements a class-level `parse` method, Grape will use it automatically. This method must take one string argument and return an instance of the correct type, or return an instance of `Grape::Types::InvalidValue` which optionally accepts a message to be returned in the response.
|
1281
1335
|
|
1282
1336
|
```ruby
|
1283
1337
|
class Color
|
@@ -1287,7 +1341,7 @@ class Color
|
|
1287
1341
|
end
|
1288
1342
|
|
1289
1343
|
def self.parse(value)
|
1290
|
-
return new(value) if %w[blue red green]
|
1344
|
+
return new(value) if %w[blue red green].include?(value)
|
1291
1345
|
|
1292
1346
|
Grape::Types::InvalidValue.new('Unsupported color')
|
1293
1347
|
end
|
@@ -1305,10 +1359,7 @@ get '/stuff' do
|
|
1305
1359
|
end
|
1306
1360
|
```
|
1307
1361
|
|
1308
|
-
Alternatively, a custom coercion method may be supplied for any type of parameter
|
1309
|
-
using `coerce_with`. Any class or object may be given that implements a `parse` or
|
1310
|
-
`call` method, in that order of precedence. The method must accept a single string
|
1311
|
-
parameter, and the return value must match the given `type`.
|
1362
|
+
Alternatively, a custom coercion method may be supplied for any type of parameter using `coerce_with`. Any class or object may be given that implements a `parse` or `call` method, in that order of precedence. The method must accept a single string parameter, and the return value must match the given `type`.
|
1312
1363
|
|
1313
1364
|
```ruby
|
1314
1365
|
params do
|
@@ -1332,9 +1383,7 @@ params do
|
|
1332
1383
|
end
|
1333
1384
|
```
|
1334
1385
|
|
1335
|
-
Grape will assert that coerced values match the given `type`, and will reject the request
|
1336
|
-
if they do not. To override this behaviour, custom types may implement a `parsed?` method
|
1337
|
-
that should accept a single argument and return `true` if the value passes type validation.
|
1386
|
+
Grape will assert that coerced values match the given `type`, and will reject the request if they do not. To override this behaviour, custom types may implement a `parsed?` method that should accept a single argument and return `true` if the value passes type validation.
|
1338
1387
|
|
1339
1388
|
```ruby
|
1340
1389
|
class SecureUri
|
@@ -1369,9 +1418,7 @@ end
|
|
1369
1418
|
|
1370
1419
|
### First-Class `JSON` Types
|
1371
1420
|
|
1372
|
-
Grape supports complex parameters given as JSON-formatted strings using the special `type: JSON`
|
1373
|
-
declaration. JSON objects and arrays of objects are accepted equally, with nested validation
|
1374
|
-
rules applied to all objects in either case:
|
1421
|
+
Grape supports complex parameters given as JSON-formatted strings using the special `type: JSON` declaration. JSON objects and arrays of objects are accepted equally, with nested validation rules applied to all objects in either case:
|
1375
1422
|
|
1376
1423
|
```ruby
|
1377
1424
|
params do
|
@@ -1390,8 +1437,7 @@ client.get('/', json: '{"int":4}') # => HTTP 400
|
|
1390
1437
|
client.get('/', json: '[{"int":4}]') # => HTTP 400
|
1391
1438
|
```
|
1392
1439
|
|
1393
|
-
Additionally `type: Array[JSON]` may be used, which explicitly marks the parameter as an array
|
1394
|
-
of objects. If a single object is supplied it will be wrapped.
|
1440
|
+
Additionally `type: Array[JSON]` may be used, which explicitly marks the parameter as an array of objects. If a single object is supplied it will be wrapped.
|
1395
1441
|
|
1396
1442
|
```ruby
|
1397
1443
|
params do
|
@@ -1403,8 +1449,7 @@ get '/' do
|
|
1403
1449
|
params[:json].each { |obj| ... } # always works
|
1404
1450
|
end
|
1405
1451
|
```
|
1406
|
-
For stricter control over the type of JSON structure which may be supplied,
|
1407
|
-
use `type: Array, coerce_with: JSON` or `type: Hash, coerce_with: JSON`.
|
1452
|
+
For stricter control over the type of JSON structure which may be supplied, use `type: Array, coerce_with: JSON` or `type: Hash, coerce_with: JSON`.
|
1408
1453
|
|
1409
1454
|
### Multiple Allowed Types
|
1410
1455
|
|
@@ -1423,8 +1468,7 @@ client.get('/', status_code: 300) # => 300
|
|
1423
1468
|
client.get('/', status_code: %w(404 NOT FOUND)) # => [404, "NOT", "FOUND"]
|
1424
1469
|
```
|
1425
1470
|
|
1426
|
-
As a special case, variant-member-type collections may also be declared, by
|
1427
|
-
passing a `Set` or `Array` with more than one member to `type`:
|
1471
|
+
As a special case, variant-member-type collections may also be declared, by passing a `Set` or `Array` with more than one member to `type`:
|
1428
1472
|
|
1429
1473
|
```ruby
|
1430
1474
|
params do
|
@@ -1440,11 +1484,8 @@ client.get('/', status_codes: %w(1 two)) # => [1, "two"]
|
|
1440
1484
|
### Validation of Nested Parameters
|
1441
1485
|
|
1442
1486
|
Parameters can be nested using `group` or by calling `requires` or `optional` with a block.
|
1443
|
-
In the [above example](#parameter-validation-and-coercion), this means `params[:media][:url]` is required along with `params[:id]`,
|
1444
|
-
and `
|
1445
|
-
With a block, `group`, `requires` and `optional` accept an additional option `type` which can
|
1446
|
-
be either `Array` or `Hash`, and defaults to `Array`. Depending on the value, the nested
|
1447
|
-
parameters will be treated either as values of a hash or as values of hashes in an array.
|
1487
|
+
In the [above example](#parameter-validation-and-coercion), this means `params[:media][:url]` is required along with `params[:id]`, and `params[:audio][:format]` is required only if `params[:audio]` is present.
|
1488
|
+
With a block, `group`, `requires` and `optional` accept an additional option `type` which can be either `Array` or `Hash`, and defaults to `Array`. Depending on the value, the nested parameters will be treated either as values of a hash or as values of hashes in an array.
|
1448
1489
|
|
1449
1490
|
```ruby
|
1450
1491
|
params do
|
@@ -1462,9 +1503,7 @@ end
|
|
1462
1503
|
|
1463
1504
|
### Dependent Parameters
|
1464
1505
|
|
1465
|
-
Suppose some of your parameters are only relevant if another parameter is given;
|
1466
|
-
Grape allows you to express this relationship through the `given` method in your
|
1467
|
-
parameters block, like so:
|
1506
|
+
Suppose some of your parameters are only relevant if another parameter is given; Grape allows you to express this relationship through the `given` method in your parameters block, like so:
|
1468
1507
|
|
1469
1508
|
```ruby
|
1470
1509
|
params do
|
@@ -1503,31 +1542,45 @@ Note: param in `given` should be the renamed one. In the example, it should be `
|
|
1503
1542
|
|
1504
1543
|
### Group Options
|
1505
1544
|
|
1506
|
-
Parameters options can be grouped. It can be useful if you want to extract
|
1507
|
-
|
1508
|
-
|
1545
|
+
Parameters options can be grouped. It can be useful if you want to extract common validation or types for several parameters.
|
1546
|
+
Within these groups, individual parameters can extend or selectively override the common settings, allowing you to maintain the defaults at the group level while still applying parameter-specific rules where necessary.
|
1547
|
+
|
1548
|
+
The example below presents a typical case when parameters share common options.
|
1509
1549
|
|
1510
1550
|
```ruby
|
1511
1551
|
params do
|
1512
|
-
requires :first_name, type: String, regexp: /w+/, desc: 'First name'
|
1513
|
-
|
1514
|
-
requires :last_name, type: String, regexp: /w+/, desc: 'Last name'
|
1552
|
+
requires :first_name, type: String, regexp: /w+/, desc: 'First name', documentation: { in: 'body' }
|
1553
|
+
optional :middle_name, type: String, regexp: /w+/, desc: 'Middle name', documentation: { in: 'body', x: { nullable: true } }
|
1554
|
+
requires :last_name, type: String, regexp: /w+/, desc: 'Last name', documentation: { in: 'body' }
|
1515
1555
|
end
|
1516
1556
|
```
|
1517
1557
|
|
1518
|
-
Grape allows you to present the same logic through the `with` method in your
|
1519
|
-
parameters block, like so:
|
1558
|
+
Grape allows you to present the same logic through the `with` method in your parameters block, like so:
|
1520
1559
|
|
1521
1560
|
```ruby
|
1522
1561
|
params do
|
1523
|
-
with(type: String, regexp: /w
|
1562
|
+
with(type: String, regexp: /w+/, documentation: { in: 'body' }) do
|
1524
1563
|
requires :first_name, desc: 'First name'
|
1525
|
-
|
1564
|
+
optional :middle_name, desc: 'Middle name', documentation: { x: { nullable: true } }
|
1526
1565
|
requires :last_name, desc: 'Last name'
|
1527
1566
|
end
|
1528
1567
|
end
|
1529
1568
|
```
|
1530
1569
|
|
1570
|
+
You can organize settings into layers using nested `with' blocks. Each layer can use, add to, or change the settings of the layer above it. This helps to keep complex parameters organized and consistent, while still allowing for specific customizations to be made.
|
1571
|
+
|
1572
|
+
```ruby
|
1573
|
+
params do
|
1574
|
+
with(documentation: { in: 'body' }) do # Applies documentation to all nested parameters
|
1575
|
+
with(type: String, regexp: /\w+/) do # Applies type and validation to names
|
1576
|
+
requires :first_name, desc: 'First name'
|
1577
|
+
requires :last_name, desc: 'Last name'
|
1578
|
+
end
|
1579
|
+
optional :age, type: Integer, desc: 'Age', documentation: { x: { nullable: true } } # Specific settings for 'age'
|
1580
|
+
end
|
1581
|
+
end
|
1582
|
+
```
|
1583
|
+
|
1531
1584
|
### Renaming
|
1532
1585
|
|
1533
1586
|
You can rename parameters using `as`, which can be useful when refactoring existing APIs:
|
@@ -1550,13 +1603,9 @@ The value passed to `as` will be the key when calling `declared(params)`.
|
|
1550
1603
|
|
1551
1604
|
#### `allow_blank`
|
1552
1605
|
|
1553
|
-
Parameters can be defined as `allow_blank`, ensuring that they contain a value. By default, `requires`
|
1554
|
-
only validates that a parameter was sent in the request, regardless its value. With `allow_blank: false`,
|
1555
|
-
empty values or whitespace only values are invalid.
|
1606
|
+
Parameters can be defined as `allow_blank`, ensuring that they contain a value. By default, `requires` only validates that a parameter was sent in the request, regardless its value. With `allow_blank: false`, empty values or whitespace only values are invalid.
|
1556
1607
|
|
1557
|
-
`allow_blank` can be combined with both `requires` and `optional`. If the parameter is required, it has to contain
|
1558
|
-
a value. If it's optional, it's possible to not send it in the request, but if it's being sent, it has to have
|
1559
|
-
some value, and not an empty string/only whitespaces.
|
1608
|
+
`allow_blank` can be combined with both `requires` and `optional`. If the parameter is required, it has to contain a value. If it's optional, it's possible to not send it in the request, but if it's being sent, it has to have some value, and not an empty string/only whitespaces.
|
1560
1609
|
|
1561
1610
|
|
1562
1611
|
```ruby
|
@@ -1593,7 +1642,7 @@ Note endless ranges are also supported with ActiveSupport >= 6.0, but they requi
|
|
1593
1642
|
```ruby
|
1594
1643
|
params do
|
1595
1644
|
requires :minimum, type: Integer, values: 10..
|
1596
|
-
optional :maximum, type: Integer, values: ..10
|
1645
|
+
optional :maximum, type: Integer, values: ..10
|
1597
1646
|
end
|
1598
1647
|
```
|
1599
1648
|
|
@@ -1607,11 +1656,9 @@ end
|
|
1607
1656
|
```
|
1608
1657
|
|
1609
1658
|
The `:values` option can also be supplied with a `Proc`, evaluated lazily with each request.
|
1610
|
-
If the Proc has arity zero (i.e. it takes no arguments) it is expected to return either a list
|
1611
|
-
or a range which will then be used to validate the parameter.
|
1659
|
+
If the Proc has arity zero (i.e. it takes no arguments) it is expected to return either a list or a range which will then be used to validate the parameter.
|
1612
1660
|
|
1613
|
-
For example, given a status model you may want to restrict by hashtags that you have
|
1614
|
-
previously defined in the `HashTag` model.
|
1661
|
+
For example, given a status model you may want to restrict by hashtags that you have previously defined in the `HashTag` model.
|
1615
1662
|
|
1616
1663
|
```ruby
|
1617
1664
|
params do
|
@@ -1619,10 +1666,7 @@ params do
|
|
1619
1666
|
end
|
1620
1667
|
```
|
1621
1668
|
|
1622
|
-
Alternatively, a Proc with arity one (i.e. taking one argument) can be used to explicitly validate
|
1623
|
-
each parameter value. In that case, the Proc is expected to return a truthy value if the parameter
|
1624
|
-
value is valid. The parameter will be considered invalid if the Proc returns a falsy value or if it
|
1625
|
-
raises a StandardError.
|
1669
|
+
Alternatively, a Proc with arity one (i.e. taking one argument) can be used to explicitly validate each parameter value. In that case, the Proc is expected to return a truthy value if the parameter value is valid. The parameter will be considered invalid if the Proc returns a falsy value or if it raises a StandardError.
|
1626
1670
|
|
1627
1671
|
```ruby
|
1628
1672
|
params do
|
@@ -1644,9 +1688,7 @@ end
|
|
1644
1688
|
|
1645
1689
|
Parameters can be restricted from having a specific set of values with the `:except_values` option.
|
1646
1690
|
|
1647
|
-
The `except_values` validator behaves similarly to the `values` validator in that it accepts either
|
1648
|
-
an Array, a Range, or a Proc. Unlike the `values` validator, however, `except_values` only accepts
|
1649
|
-
Procs with arity zero.
|
1691
|
+
The `except_values` validator behaves similarly to the `values` validator in that it accepts either an Array, a Range, or a Proc. Unlike the `values` validator, however, `except_values` only accepts Procs with arity zero.
|
1650
1692
|
|
1651
1693
|
```ruby
|
1652
1694
|
params do
|
@@ -1667,11 +1709,23 @@ params do
|
|
1667
1709
|
end
|
1668
1710
|
```
|
1669
1711
|
|
1712
|
+
#### `length`
|
1713
|
+
|
1714
|
+
Parameters with types that support `#length` method can be restricted to have a specific length with the `:length` option.
|
1715
|
+
|
1716
|
+
The validator accepts `:min` or `:max` or both options to validate that the value of the parameter is within the given limits.
|
1717
|
+
|
1718
|
+
```ruby
|
1719
|
+
params do
|
1720
|
+
requires :str, type: String, length: { min: 3 }
|
1721
|
+
requires :list, type: [Integer], length: { min: 3, max: 5 }
|
1722
|
+
requires :hash, type: Hash, length: { max: 5 }
|
1723
|
+
end
|
1724
|
+
```
|
1725
|
+
|
1670
1726
|
#### `regexp`
|
1671
1727
|
|
1672
|
-
Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value
|
1673
|
-
does not match the regular expression an error will be returned. Note that this is true for both `requires`
|
1674
|
-
and `optional` parameters.
|
1728
|
+
Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value does not match the regular expression an error will be returned. Note that this is true for both `requires` and `optional` parameters.
|
1675
1729
|
|
1676
1730
|
```ruby
|
1677
1731
|
params do
|
@@ -1806,8 +1860,7 @@ namespace :statuses do
|
|
1806
1860
|
end
|
1807
1861
|
```
|
1808
1862
|
|
1809
|
-
The `namespace` method has a number of aliases, including: `group`, `resource`,
|
1810
|
-
`resources`, and `segment`. Use whichever reads the best for your API.
|
1863
|
+
The `namespace` method has a number of aliases, including: `group`, `resource`, `resources`, and `segment`. Use whichever reads the best for your API.
|
1811
1864
|
|
1812
1865
|
You can conveniently define a route parameter as a namespace using `route_param`.
|
1813
1866
|
|
@@ -1962,8 +2015,7 @@ end
|
|
1962
2015
|
|
1963
2016
|
### I18n
|
1964
2017
|
|
1965
|
-
Grape supports I18n for parameter-related error messages, but will fallback to English if
|
1966
|
-
translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
|
2018
|
+
Grape supports I18n for parameter-related error messages, but will fallback to English if translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
|
1967
2019
|
|
1968
2020
|
In case your app enforces available locales only and :en is not included in your available locales, Grape cannot fall back to English and will return the translation key for the error message. To avoid this behaviour, either provide a translation for your default locale or add :en to your available locales.
|
1969
2021
|
|
@@ -1988,6 +2040,15 @@ params do
|
|
1988
2040
|
end
|
1989
2041
|
```
|
1990
2042
|
|
2043
|
+
#### `length`
|
2044
|
+
|
2045
|
+
```ruby
|
2046
|
+
params do
|
2047
|
+
requires :str, type: String, length: { min: 5, message: 'str is expected to be atleast 5 characters long' }
|
2048
|
+
requires :list, type: [Integer], length: { min: 2, max: 3, message: 'list is expected to have between 2 and 3 elements' }
|
2049
|
+
end
|
2050
|
+
```
|
2051
|
+
|
1991
2052
|
#### `all_or_none_of`
|
1992
2053
|
|
1993
2054
|
```ruby
|
@@ -2096,6 +2157,40 @@ params do
|
|
2096
2157
|
end
|
2097
2158
|
```
|
2098
2159
|
|
2160
|
+
### Using `dry-validation` or `dry-schema`
|
2161
|
+
|
2162
|
+
As an alternative to the `params` DSL described above, you can use a schema or `dry-validation` contract to describe an endpoint's parameters. This can be especially useful if you use the above already in some other parts of your application. If not, you'll need to add `dry-validation` or `dry-schema` to your `Gemfile`.
|
2163
|
+
|
2164
|
+
Then call `contract` with a contract or schema defined previously:
|
2165
|
+
|
2166
|
+
```rb
|
2167
|
+
CreateOrdersSchema = Dry::Schema.Params do
|
2168
|
+
required(:orders).array(:hash) do
|
2169
|
+
required(:name).filled(:string)
|
2170
|
+
optional(:volume).maybe(:integer, lt?: 9)
|
2171
|
+
end
|
2172
|
+
end
|
2173
|
+
|
2174
|
+
# ...
|
2175
|
+
|
2176
|
+
contract CreateOrdersSchema
|
2177
|
+
```
|
2178
|
+
|
2179
|
+
or with a block, using the [schema definition syntax](https://dry-rb.org/gems/dry-schema/1.13/#quick-start):
|
2180
|
+
|
2181
|
+
```rb
|
2182
|
+
contract do
|
2183
|
+
required(:orders).array(:hash) do
|
2184
|
+
required(:name).filled(:string)
|
2185
|
+
optional(:volume).maybe(:integer, lt?: 9)
|
2186
|
+
end
|
2187
|
+
end
|
2188
|
+
```
|
2189
|
+
|
2190
|
+
The latter will define a coercing schema (`Dry::Schema.Params`). When using the former approach, it's up to you to decide whether the input will need coercing.
|
2191
|
+
|
2192
|
+
The `params` and `contract` declarations can also be used together in the same API, e.g. to describe different parts of a nested namespace for an endpoint.
|
2193
|
+
|
2099
2194
|
## Headers
|
2100
2195
|
|
2101
2196
|
### Request
|
@@ -2123,8 +2218,9 @@ curl -H "secret_PassWord: swordfish" ...
|
|
2123
2218
|
|
2124
2219
|
The header name will have been normalized for you.
|
2125
2220
|
|
2126
|
-
- In the `header` helper names will be coerced into a
|
2127
|
-
- In the `
|
2221
|
+
- In the `header` helper names will be coerced into a downcased kebab case as `secret-password` if using Rack 3.
|
2222
|
+
- In the `header` helper names will be coerced into a capitalized kebab case as `Secret-PassWord` if using Rack < 3.
|
2223
|
+
- In the `env` collection they appear in all uppercase, in snake case, and prefixed with 'HTTP_' as `HTTP_SECRET_PASSWORD`
|
2128
2224
|
|
2129
2225
|
The header name will have been normalized per HTTP standards defined in [RFC2616 Section 4.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2) regardless of what is being sent by a client.
|
2130
2226
|
|
@@ -2194,8 +2290,7 @@ namespace ':id' do
|
|
2194
2290
|
end
|
2195
2291
|
```
|
2196
2292
|
|
2197
|
-
Optionally, you can define requirements for your named route parameters using regular
|
2198
|
-
expressions on namespace or endpoint. The route will match only if all requirements are met.
|
2293
|
+
Optionally, you can define requirements for your named route parameters using regular expressions on namespace or endpoint. The route will match only if all requirements are met.
|
2199
2294
|
|
2200
2295
|
```ruby
|
2201
2296
|
get ':id', requirements: { id: /[0-9]*/ } do
|
@@ -2213,8 +2308,7 @@ end
|
|
2213
2308
|
|
2214
2309
|
## Helpers
|
2215
2310
|
|
2216
|
-
You can define helper methods that your endpoints can use with the `helpers`
|
2217
|
-
macro by either giving a block or an array of modules.
|
2311
|
+
You can define helper methods that your endpoints can use with the `helpers` macro by either giving a block or an array of modules.
|
2218
2312
|
|
2219
2313
|
```ruby
|
2220
2314
|
module StatusHelpers
|
@@ -2453,11 +2547,36 @@ end
|
|
2453
2547
|
API.recognize_path '/statuses'
|
2454
2548
|
```
|
2455
2549
|
|
2550
|
+
Since version `2.1.0`, the `recognize_path` method takes into account the parameters type to determine which endpoint should match with given path.
|
2551
|
+
|
2552
|
+
```ruby
|
2553
|
+
class Books < Grape::API
|
2554
|
+
resource :books do
|
2555
|
+
route_param :id, type: Integer do
|
2556
|
+
# GET /books/:id
|
2557
|
+
get do
|
2558
|
+
#...
|
2559
|
+
end
|
2560
|
+
end
|
2561
|
+
|
2562
|
+
resource :share do
|
2563
|
+
# POST /books/share
|
2564
|
+
post do
|
2565
|
+
# ....
|
2566
|
+
end
|
2567
|
+
end
|
2568
|
+
end
|
2569
|
+
end
|
2570
|
+
|
2571
|
+
API.recognize_path '/books/1' # => /books/:id
|
2572
|
+
API.recognize_path '/books/share' # => /books/share
|
2573
|
+
API.recognize_path '/books/other' # => nil
|
2574
|
+
```
|
2575
|
+
|
2576
|
+
|
2456
2577
|
## Allowed Methods
|
2457
2578
|
|
2458
|
-
When you add a `GET` route for a resource, a route for the `HEAD`
|
2459
|
-
method will also be added automatically. You can disable this
|
2460
|
-
behavior with `do_not_route_head!`.
|
2579
|
+
When you add a `GET` route for a resource, a route for the `HEAD` method will also be added automatically. You can disable this behavior with `do_not_route_head!`.
|
2461
2580
|
|
2462
2581
|
``` ruby
|
2463
2582
|
class API < Grape::API
|
@@ -2469,11 +2588,7 @@ class API < Grape::API
|
|
2469
2588
|
end
|
2470
2589
|
```
|
2471
2590
|
|
2472
|
-
When you add a route for a resource, a route for the `OPTIONS`
|
2473
|
-
method will also be added. The response to an OPTIONS request will
|
2474
|
-
include an "Allow" header listing the supported methods. If the resource
|
2475
|
-
has `before` and `after` callbacks they will be executed, but no other callbacks will
|
2476
|
-
run.
|
2591
|
+
When you add a route for a resource, a route for the `OPTIONS` method will also be added. The response to an OPTIONS request will include an "Allow" header listing the supported methods. If the resource has `before` and `after` callbacks they will be executed, but no other callbacks will run.
|
2477
2592
|
|
2478
2593
|
```ruby
|
2479
2594
|
class API < Grape::API
|
@@ -2502,10 +2617,7 @@ curl -v -X OPTIONS http://localhost:3000/rt_count
|
|
2502
2617
|
|
2503
2618
|
You can disable this behavior with `do_not_route_options!`.
|
2504
2619
|
|
2505
|
-
If a request for a resource is made with an unsupported HTTP method, an
|
2506
|
-
HTTP 405 (Method Not Allowed) response will be returned. If the resource
|
2507
|
-
has `before` callbacks they will be executed, but no other callbacks will
|
2508
|
-
run.
|
2620
|
+
If a request for a resource is made with an unsupported HTTP method, an HTTP 405 (Method Not Allowed) response will be returned. If the resource has `before` callbacks they will be executed, but no other callbacks will run.
|
2509
2621
|
|
2510
2622
|
``` shell
|
2511
2623
|
curl -X DELETE -v http://localhost:3000/rt_count/
|
@@ -2531,8 +2643,7 @@ Anything that responds to `#to_s` can be given as a first argument to `error!`.
|
|
2531
2643
|
error! :not_found, 404
|
2532
2644
|
```
|
2533
2645
|
|
2534
|
-
You can also return JSON formatted objects by raising error! and passing a hash
|
2535
|
-
instead of a message.
|
2646
|
+
You can also return JSON formatted objects by raising error! and passing a hash instead of a message.
|
2536
2647
|
|
2537
2648
|
```ruby
|
2538
2649
|
error!({ error: 'unexpected error', detail: 'missing widget' }, 500)
|
@@ -2597,8 +2708,7 @@ route :any, '*path' do
|
|
2597
2708
|
end
|
2598
2709
|
```
|
2599
2710
|
|
2600
|
-
It is very crucial to __define this endpoint at the very end of your API__, as it
|
2601
|
-
literally accepts every request.
|
2711
|
+
It is very crucial to __define this endpoint at the very end of your API__, as it literally accepts every request.
|
2602
2712
|
|
2603
2713
|
## Exception Handling
|
2604
2714
|
|
@@ -2840,33 +2950,11 @@ Any exception that is not subclass of `StandardError` should be rescued explicit
|
|
2840
2950
|
Usually it is not a case for an application logic as such errors point to problems in Ruby runtime.
|
2841
2951
|
This is following [standard recommendations for exceptions handling](https://ruby-doc.org/core/Exception.html).
|
2842
2952
|
|
2843
|
-
### Rails 3.x
|
2844
|
-
|
2845
|
-
When mounted inside containers, such as Rails 3.x, errors such as "404 Not Found" or
|
2846
|
-
"406 Not Acceptable" will likely be handled and rendered by Rails handlers. For instance,
|
2847
|
-
accessing a nonexistent route "/api/foo" raises a 404, which inside rails will ultimately
|
2848
|
-
be translated to an `ActionController::RoutingError`, which most likely will get rendered
|
2849
|
-
to a HTML error page.
|
2850
|
-
|
2851
|
-
Most APIs will enjoy preventing downstream handlers from handling errors. You may set the
|
2852
|
-
`:cascade` option to `false` for the entire API or separately on specific `version` definitions,
|
2853
|
-
which will remove the `X-Cascade: true` header from API responses.
|
2854
|
-
|
2855
|
-
```ruby
|
2856
|
-
cascade false
|
2857
|
-
```
|
2858
|
-
|
2859
|
-
```ruby
|
2860
|
-
version 'v1', using: :header, vendor: 'twitter', cascade: false
|
2861
|
-
```
|
2862
|
-
|
2863
2953
|
## Logging
|
2864
2954
|
|
2865
|
-
`Grape::API` provides a `logger` method which by default will return an instance of the `Logger`
|
2866
|
-
class from Ruby's standard library.
|
2955
|
+
`Grape::API` provides a `logger` method which by default will return an instance of the `Logger` class from Ruby's standard library.
|
2867
2956
|
|
2868
|
-
To log messages from within an endpoint, you need to define a helper to make the logger
|
2869
|
-
available in the endpoint context.
|
2957
|
+
To log messages from within an endpoint, you need to define a helper to make the logger available in the endpoint context.
|
2870
2958
|
|
2871
2959
|
```ruby
|
2872
2960
|
class API < Grape::API
|
@@ -2915,9 +3003,7 @@ For similar to Rails request logging try the [grape_logging](https://github.com/
|
|
2915
3003
|
|
2916
3004
|
## API Formats
|
2917
3005
|
|
2918
|
-
Your API can declare which content-types to support by using `content_type`. If you do not specify any, Grape will support
|
2919
|
-
_XML_, _JSON_, _BINARY_, and _TXT_ content-types. The default format is `:txt`; you can change this with `default_format`.
|
2920
|
-
Essentially, the two APIs below are equivalent.
|
3006
|
+
Your API can declare which content-types to support by using `content_type`. If you do not specify any, Grape will support _XML_, _JSON_, _BINARY_, and _TXT_ content-types. The default format is `:txt`; you can change this with `default_format`. Essentially, the two APIs below are equivalent.
|
2921
3007
|
|
2922
3008
|
```ruby
|
2923
3009
|
class Twitter::API < Grape::API
|
@@ -2936,9 +3022,7 @@ class Twitter::API < Grape::API
|
|
2936
3022
|
end
|
2937
3023
|
```
|
2938
3024
|
|
2939
|
-
If you declare any `content_type` whatsoever, the Grape defaults will be overridden. For example, the following API will only
|
2940
|
-
support the `:xml` and `:rss` content-types, but not `:txt`, `:json`, or `:binary`. Importantly, this means the `:txt`
|
2941
|
-
default format is not supported! So, make sure to set a new `default_format`.
|
3025
|
+
If you declare any `content_type` whatsoever, the Grape defaults will be overridden. For example, the following API will only support the `:xml` and `:rss` content-types, but not `:txt`, `:json`, or `:binary`. Importantly, this means the `:txt` default format is not supported! So, make sure to set a new `default_format`.
|
2942
3026
|
|
2943
3027
|
```ruby
|
2944
3028
|
class Twitter::API < Grape::API
|
@@ -2949,8 +3033,7 @@ class Twitter::API < Grape::API
|
|
2949
3033
|
end
|
2950
3034
|
```
|
2951
3035
|
|
2952
|
-
Serialization takes place automatically. For example, you do not have to call `to_json` in each JSON API endpoint
|
2953
|
-
implementation. The response format (and thus the automatic serialization) is determined in the following order:
|
3036
|
+
Serialization takes place automatically. For example, you do not have to call `to_json` in each JSON API endpoint implementation. The response format (and thus the automatic serialization) is determined in the following order:
|
2954
3037
|
* Use the file extension, if specified. If the file is .json, choose the JSON format.
|
2955
3038
|
* Use the value of the `format` parameter in the query string, if specified.
|
2956
3039
|
* Use the format set by the `format` option, if specified.
|
@@ -2973,18 +3056,13 @@ class MultipleFormatAPI < Grape::API
|
|
2973
3056
|
end
|
2974
3057
|
```
|
2975
3058
|
|
2976
|
-
* `GET /hello` (with an `Accept: */*` header) does not have an extension or a `format` parameter, so it will respond with
|
2977
|
-
JSON (the default format).
|
3059
|
+
* `GET /hello` (with an `Accept: */*` header) does not have an extension or a `format` parameter, so it will respond with JSON (the default format).
|
2978
3060
|
* `GET /hello.xml` has a recognized extension, so it will respond with XML.
|
2979
3061
|
* `GET /hello?format=xml` has a recognized `format` parameter, so it will respond with XML.
|
2980
|
-
* `GET /hello.xml?format=json` has a recognized extension (which takes precedence over the `format` parameter), so it will
|
2981
|
-
|
2982
|
-
* `GET /hello.xls`
|
2983
|
-
|
2984
|
-
* `GET /hello.xls` with an `Accept: application/xml` header has an unrecognized extension, but the `Accept` header
|
2985
|
-
corresponds to a recognized format, so it will respond with XML.
|
2986
|
-
* `GET /hello.xls` with an `Accept: text/plain` header has an unrecognized extension *and* an unrecognized `Accept` header,
|
2987
|
-
so it will respond with JSON (the default format).
|
3062
|
+
* `GET /hello.xml?format=json` has a recognized extension (which takes precedence over the `format` parameter), so it will respond with XML.
|
3063
|
+
* `GET /hello.xls` (with an `Accept: */*` header) has an extension, but that extension is not recognized, so it will respond with JSON (the default format).
|
3064
|
+
* `GET /hello.xls` with an `Accept: application/xml` header has an unrecognized extension, but the `Accept` header corresponds to a recognized format, so it will respond with XML.
|
3065
|
+
* `GET /hello.xls` with an `Accept: text/plain` header has an unrecognized extension *and* an unrecognized `Accept` header, so it will respond with JSON (the default format).
|
2988
3066
|
|
2989
3067
|
You can override this process explicitly by specifying `env['api.format']` in the API itself.
|
2990
3068
|
For example, the following API will let you upload arbitrary files and return their contents as an attachment with the correct MIME type.
|
@@ -3001,8 +3079,7 @@ class Twitter::API < Grape::API
|
|
3001
3079
|
end
|
3002
3080
|
```
|
3003
3081
|
|
3004
|
-
You can have your API only respond to a single format with `format`. If you use this, the API will **not** respond to file
|
3005
|
-
extensions other than specified in `format`. For example, consider the following API.
|
3082
|
+
You can have your API only respond to a single format with `format`. If you use this, the API will **not** respond to file extensions other than specified in `format`. For example, consider the following API.
|
3006
3083
|
|
3007
3084
|
```ruby
|
3008
3085
|
class SingleFormatAPI < Grape::API
|
@@ -3017,14 +3094,10 @@ end
|
|
3017
3094
|
* `GET /hello` will respond with JSON.
|
3018
3095
|
* `GET /hello.json` will respond with JSON.
|
3019
3096
|
* `GET /hello.xml`, `GET /hello.foobar`, or *any* other extension will respond with an HTTP 404 error code.
|
3020
|
-
* `GET /hello?format=xml` will respond with an HTTP 406 error code, because the XML format specified by the request parameter
|
3021
|
-
|
3022
|
-
* `GET /hello` with an `Accept: application/xml` header will still respond with JSON, since it could not negotiate a
|
3023
|
-
recognized content-type from the headers and JSON is the effective default.
|
3097
|
+
* `GET /hello?format=xml` will respond with an HTTP 406 error code, because the XML format specified by the request parameter is not supported.
|
3098
|
+
* `GET /hello` with an `Accept: application/xml` header will still respond with JSON, since it could not negotiate a recognized content-type from the headers and JSON is the effective default.
|
3024
3099
|
|
3025
|
-
The formats apply to parsing, too. The following API will only respond to the JSON content-type and will not parse any other
|
3026
|
-
input than `application/json`, `application/x-www-form-urlencoded`, `multipart/form-data`, `multipart/related` and
|
3027
|
-
`multipart/mixed`. All other requests will fail with an HTTP 406 error code.
|
3100
|
+
The formats apply to parsing, too. The following API will only respond to the JSON content-type and will not parse any other input than `application/json`, `application/x-www-form-urlencoded`, `multipart/form-data`, `multipart/related` and `multipart/mixed`. All other requests will fail with an HTTP 406 error code.
|
3028
3101
|
|
3029
3102
|
```ruby
|
3030
3103
|
class Twitter::API < Grape::API
|
@@ -3080,23 +3153,18 @@ end
|
|
3080
3153
|
Built-in formatters are the following.
|
3081
3154
|
|
3082
3155
|
* `:json`: use object's `to_json` when available, otherwise call `MultiJson.dump`
|
3083
|
-
* `:xml`: use object's `to_xml` when available, usually via `MultiXml
|
3156
|
+
* `:xml`: use object's `to_xml` when available, usually via `MultiXml`
|
3084
3157
|
* `:txt`: use object's `to_txt` when available, otherwise `to_s`
|
3085
3158
|
* `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`
|
3086
3159
|
* `:binary`: data will be returned "as is"
|
3087
3160
|
|
3088
|
-
If a body is present in a request to an API, with a Content-Type header value that is of an unsupported type a
|
3089
|
-
"415 Unsupported Media Type" error code will be returned by Grape.
|
3161
|
+
If a body is present in a request to an API, with a Content-Type header value that is of an unsupported type a "415 Unsupported Media Type" error code will be returned by Grape.
|
3090
3162
|
|
3091
|
-
Response statuses that indicate no content as defined by [Rack](https://github.com/rack)
|
3092
|
-
[here](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
|
3093
|
-
will bypass serialization and the body entity - though there should be none -
|
3094
|
-
will not be modified.
|
3163
|
+
Response statuses that indicate no content as defined by [Rack](https://github.com/rack) [here](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567) will bypass serialization and the body entity - though there should be none - will not be modified.
|
3095
3164
|
|
3096
3165
|
### JSONP
|
3097
3166
|
|
3098
|
-
Grape supports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the
|
3099
|
-
[rack-contrib](https://github.com/rack/rack-contrib) gem. Add `rack-contrib` to your `Gemfile`.
|
3167
|
+
Grape supports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the [rack-contrib](https://github.com/rack/rack-contrib) gem. Add `rack-contrib` to your `Gemfile`.
|
3100
3168
|
|
3101
3169
|
```ruby
|
3102
3170
|
require 'rack/contrib'
|
@@ -3112,9 +3180,7 @@ end
|
|
3112
3180
|
|
3113
3181
|
### CORS
|
3114
3182
|
|
3115
|
-
Grape supports CORS via [Rack::CORS](https://github.com/cyu/rack-cors), part of the
|
3116
|
-
[rack-cors](https://github.com/cyu/rack-cors) gem. Add `rack-cors` to your `Gemfile`,
|
3117
|
-
then use the middleware in your config.ru file.
|
3183
|
+
Grape supports CORS via [Rack::CORS](https://github.com/cyu/rack-cors), part of the [rack-cors](https://github.com/cyu/rack-cors) gem. Add `rack-cors` to your `Gemfile`, then use the middleware in your config.ru file.
|
3118
3184
|
|
3119
3185
|
```ruby
|
3120
3186
|
require 'rack/cors'
|
@@ -3132,8 +3198,7 @@ run Twitter::API
|
|
3132
3198
|
|
3133
3199
|
## Content-type
|
3134
3200
|
|
3135
|
-
Content-type is set by the formatter. You can override the content-type of the response at runtime
|
3136
|
-
by setting the `Content-Type` header.
|
3201
|
+
Content-type is set by the formatter. You can override the content-type of the response at runtime by setting the `Content-Type` header.
|
3137
3202
|
|
3138
3203
|
```ruby
|
3139
3204
|
class API < Grape::API
|
@@ -3146,16 +3211,12 @@ end
|
|
3146
3211
|
|
3147
3212
|
## API Data Formats
|
3148
3213
|
|
3149
|
-
Grape accepts and parses input data sent with the POST and PUT methods as described in the Parameters
|
3150
|
-
section above. It also supports custom data formats. You must declare additional content-types via
|
3151
|
-
`content_type` and optionally supply a parser via `parser` unless a parser is already available within
|
3152
|
-
Grape to enable a custom format. Such a parser can be a function or a class.
|
3214
|
+
Grape accepts and parses input data sent with the POST and PUT methods as described in the Parameters section above. It also supports custom data formats. You must declare additional content-types via `content_type` and optionally supply a parser via `parser` unless a parser is already available within Grape to enable a custom format. Such a parser can be a function or a class.
|
3153
3215
|
|
3154
3216
|
With a parser, parsed data is available "as-is" in `env['api.request.body']`.
|
3155
3217
|
Without a parser, data is available "as-is" and in `env['api.request.input']`.
|
3156
3218
|
|
3157
|
-
The following example is a trivial parser that will assign any input with the "text/custom" content-type
|
3158
|
-
to `:value`. The parameter will be available via `params[:value]` inside the API call.
|
3219
|
+
The following example is a trivial parser that will assign any input with the "text/custom" content-type to `:value`. The parameter will be available via `params[:value]` inside the API call.
|
3159
3220
|
|
3160
3221
|
```ruby
|
3161
3222
|
module CustomParser
|
@@ -3189,9 +3250,7 @@ Grape uses `JSON` and `ActiveSupport::XmlMini` for JSON and XML parsing by defau
|
|
3189
3250
|
|
3190
3251
|
## RESTful Model Representations
|
3191
3252
|
|
3192
|
-
Grape supports a range of ways to present your data with some help from a generic `present` method,
|
3193
|
-
which accepts two arguments: the object to be presented and the options associated with it. The options
|
3194
|
-
hash may include `:with`, which defines the entity to expose.
|
3253
|
+
Grape supports a range of ways to present your data with some help from a generic `present` method, which accepts two arguments: the object to be presented and the options associated with it. The options hash may include `:with`, which defines the entity to expose.
|
3195
3254
|
|
3196
3255
|
### Grape Entities
|
3197
3256
|
|
@@ -3270,8 +3329,7 @@ The response will be
|
|
3270
3329
|
}
|
3271
3330
|
```
|
3272
3331
|
|
3273
|
-
In addition to separately organizing entities, it may be useful to put them as namespaced
|
3274
|
-
classes underneath the model they represent.
|
3332
|
+
In addition to separately organizing entities, it may be useful to put them as namespaced classes underneath the model they represent.
|
3275
3333
|
|
3276
3334
|
```ruby
|
3277
3335
|
class Status
|
@@ -3285,11 +3343,7 @@ class Status
|
|
3285
3343
|
end
|
3286
3344
|
```
|
3287
3345
|
|
3288
|
-
If you organize your entities this way, Grape will automatically detect the `Entity` class and
|
3289
|
-
use it to present your models. In this example, if you added `present Status.new` to your endpoint,
|
3290
|
-
Grape will automatically detect that there is a `Status::Entity` class and use that as the
|
3291
|
-
representative entity. This can still be overridden by using the `:with` option or an explicit
|
3292
|
-
`represents` call.
|
3346
|
+
If you organize your entities this way, Grape will automatically detect the `Entity` class and use it to present your models. In this example, if you added `present Status.new` to your endpoint, Grape will automatically detect that there is a `Status::Entity` class and use that as the representative entity. This can still be overridden by using the `:with` option or an explicit `represents` call.
|
3293
3347
|
|
3294
3348
|
You can present `hash` with `Grape::Presenters::Presenter` to keep things consistent.
|
3295
3349
|
|
@@ -3322,15 +3376,11 @@ You can use [Roar](https://github.com/apotonick/roar) to render HAL or Collectio
|
|
3322
3376
|
|
3323
3377
|
### Rabl
|
3324
3378
|
|
3325
|
-
You can use [Rabl](https://github.com/nesquena/rabl) templates with the help of the
|
3326
|
-
[grape-rabl](https://github.com/ruby-grape/grape-rabl) gem, which defines a custom Grape Rabl
|
3327
|
-
formatter.
|
3379
|
+
You can use [Rabl](https://github.com/nesquena/rabl) templates with the help of the [grape-rabl](https://github.com/ruby-grape/grape-rabl) gem, which defines a custom Grape Rabl formatter.
|
3328
3380
|
|
3329
3381
|
### Active Model Serializers
|
3330
3382
|
|
3331
|
-
You can use [Active Model Serializers](https://github.com/rails-api/active_model_serializers) serializers with the help of the
|
3332
|
-
[grape-active_model_serializers](https://github.com/jrhe/grape-active_model_serializers) gem, which defines a custom Grape AMS
|
3333
|
-
formatter.
|
3383
|
+
You can use [Active Model Serializers](https://github.com/rails-api/active_model_serializers) serializers with the help of the [grape-active_model_serializers](https://github.com/jrhe/grape-active_model_serializers) gem, which defines a custom Grape AMS formatter.
|
3334
3384
|
|
3335
3385
|
## Sending Raw or No Data
|
3336
3386
|
|
@@ -3370,9 +3420,7 @@ class API < Grape::API
|
|
3370
3420
|
end
|
3371
3421
|
```
|
3372
3422
|
|
3373
|
-
You can also set the response to a file with `sendfile`. This works with the
|
3374
|
-
[Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send
|
3375
|
-
the file through your web server software.
|
3423
|
+
You can also set the response to a file with `sendfile`. This works with the [Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send the file through your web server software.
|
3376
3424
|
|
3377
3425
|
```ruby
|
3378
3426
|
class API < Grape::API
|
@@ -3414,11 +3462,9 @@ end
|
|
3414
3462
|
|
3415
3463
|
## Authentication
|
3416
3464
|
|
3417
|
-
### Basic
|
3465
|
+
### Basic Auth
|
3418
3466
|
|
3419
|
-
Grape has built-in Basic
|
3420
|
-
is executed in the context of the current `Endpoint`). Authentication
|
3421
|
-
applies to the current namespace and any children, but not parents.
|
3467
|
+
Grape has built-in Basic authentication (the given `block` is executed in the context of the current `Endpoint`). Authentication applies to the current namespace and any children, but not parents.
|
3422
3468
|
|
3423
3469
|
```ruby
|
3424
3470
|
http_basic do |username, password|
|
@@ -3427,32 +3473,15 @@ http_basic do |username, password|
|
|
3427
3473
|
end
|
3428
3474
|
```
|
3429
3475
|
|
3430
|
-
Digest auth supports clear-text passwords and password hashes.
|
3431
|
-
|
3432
|
-
```ruby
|
3433
|
-
http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
|
3434
|
-
# lookup the user's password here
|
3435
|
-
end
|
3436
|
-
```
|
3437
|
-
|
3438
|
-
```ruby
|
3439
|
-
http_digest(realm: { realm: 'Test Api', opaque: 'app secret', passwords_hashed: true }) do |username|
|
3440
|
-
# lookup the user's password hash here
|
3441
|
-
end
|
3442
|
-
```
|
3443
|
-
|
3444
3476
|
### Register custom middleware for authentication
|
3445
3477
|
|
3446
|
-
Grape can use custom Middleware for authentication. How to implement these
|
3447
|
-
Middleware have a look at `Rack::Auth::Basic` or similar implementations.
|
3448
|
-
|
3478
|
+
Grape can use custom Middleware for authentication. How to implement these Middleware have a look at `Rack::Auth::Basic` or similar implementations.
|
3449
3479
|
|
3450
3480
|
For registering a Middleware you need the following options:
|
3451
3481
|
|
3452
3482
|
* `label` - the name for your authenticator to use it later
|
3453
3483
|
* `MiddlewareClass` - the MiddlewareClass to use for authentication
|
3454
|
-
* `option_lookup_proc` - A Proc with one Argument to lookup the options at
|
3455
|
-
runtime (return value is an `Array` as Parameter for the Middleware).
|
3484
|
+
* `option_lookup_proc` - A Proc with one Argument to lookup the options at runtime (return value is an `Array` as Parameter for the Middleware).
|
3456
3485
|
|
3457
3486
|
Example:
|
3458
3487
|
|
@@ -3476,7 +3505,7 @@ You can access the controller params, headers, and helpers through the context w
|
|
3476
3505
|
|
3477
3506
|
Grape routes can be reflected at runtime. This can notably be useful for generating documentation.
|
3478
3507
|
|
3479
|
-
Grape exposes arrays of API versions and compiled routes. Each route contains a `
|
3508
|
+
Grape exposes arrays of API versions and compiled routes. Each route contains a `prefix`, `version`, `namespace`, `method` and `params`. You can add custom route settings to the route metadata with `route_setting`.
|
3480
3509
|
|
3481
3510
|
```ruby
|
3482
3511
|
class TwitterAPI < Grape::API
|
@@ -3499,7 +3528,7 @@ TwitterAPI::routes[0].description # => 'Includes custom settings.'
|
|
3499
3528
|
TwitterAPI::routes[0].settings[:custom] # => { key: 'value' }
|
3500
3529
|
```
|
3501
3530
|
|
3502
|
-
Note that `Route#route_xyz` methods have been deprecated since 0.15.0.
|
3531
|
+
Note that `Route#route_xyz` methods have been deprecated since 0.15.0 and removed since 2.0.1.
|
3503
3532
|
|
3504
3533
|
Please use `Route#xyz` instead.
|
3505
3534
|
|
@@ -3519,15 +3548,12 @@ class MyAPI < Grape::API
|
|
3519
3548
|
requires :id, type: Integer, desc: 'Identity.'
|
3520
3549
|
end
|
3521
3550
|
get 'params/:id' do
|
3522
|
-
route.
|
3551
|
+
route.params[params[:id]] # yields the parameter description
|
3523
3552
|
end
|
3524
3553
|
end
|
3525
3554
|
```
|
3526
3555
|
|
3527
|
-
The current endpoint responding to the request is `self` within the API block
|
3528
|
-
or `env['api.endpoint']` elsewhere. The endpoint has some interesting properties,
|
3529
|
-
such as `source` which gives you access to the original code block of the API
|
3530
|
-
implementation. This can be particularly useful for building a logger middleware.
|
3556
|
+
The current endpoint responding to the request is `self` within the API block or `env['api.endpoint']` elsewhere. The endpoint has some interesting properties, such as `source` which gives you access to the original code block of the API implementation. This can be particularly useful for building a logger middleware.
|
3531
3557
|
|
3532
3558
|
```ruby
|
3533
3559
|
class ApiLogger < Grape::Middleware::Base
|
@@ -3541,10 +3567,8 @@ end
|
|
3541
3567
|
|
3542
3568
|
## Before, After and Finally
|
3543
3569
|
|
3544
|
-
Blocks can be executed before or after every API call, using `before`, `after`,
|
3545
|
-
`
|
3546
|
-
If the API fails the `after` call will not be triggered, if you need code to execute for sure
|
3547
|
-
use the `finally`.
|
3570
|
+
Blocks can be executed before or after every API call, using `before`, `after`, `before_validation` and `after_validation`.
|
3571
|
+
If the API fails the `after` call will not be triggered, if you need code to execute for sure use the `finally`.
|
3548
3572
|
|
3549
3573
|
Before and after callbacks execute in the following order:
|
3550
3574
|
|
@@ -3558,13 +3582,9 @@ Before and after callbacks execute in the following order:
|
|
3558
3582
|
|
3559
3583
|
Steps 4, 5 and 6 only happen if validation succeeds.
|
3560
3584
|
|
3561
|
-
If a request for a resource is made with an unsupported HTTP method (returning
|
3562
|
-
HTTP 405) only `before` callbacks will be executed. The remaining callbacks will
|
3563
|
-
be bypassed.
|
3585
|
+
If a request for a resource is made with an unsupported HTTP method (returning HTTP 405) only `before` callbacks will be executed. The remaining callbacks will be bypassed.
|
3564
3586
|
|
3565
|
-
If a request for a resource is made that triggers the built-in `OPTIONS` handler,
|
3566
|
-
only `before` and `after` callbacks will be executed. The remaining callbacks will
|
3567
|
-
be bypassed.
|
3587
|
+
If a request for a resource is made that triggers the built-in `OPTIONS` handler, only `before` and `after` callbacks will be executed. The remaining callbacks will be bypassed.
|
3568
3588
|
|
3569
3589
|
For example, using a simple `before` block to set a header.
|
3570
3590
|
|
@@ -3709,11 +3729,7 @@ Instead of altering a response, you can also terminate and rewrite it from any c
|
|
3709
3729
|
|
3710
3730
|
## Anchoring
|
3711
3731
|
|
3712
|
-
Grape by default anchors all request paths, which means that the request URL
|
3713
|
-
should match from start to end to match, otherwise a `404 Not Found` is
|
3714
|
-
returned. However, this is sometimes not what you want, because it is not always
|
3715
|
-
known upfront what can be expected from the call. This is because Rack-mount by
|
3716
|
-
default anchors requests to match from the start to the end, or not at all.
|
3732
|
+
Grape by default anchors all request paths, which means that the request URL should match from start to end to match, otherwise a `404 Not Found` is returned. However, this is sometimes not what you want, because it is not always known upfront what can be expected from the call. This is because Rack-mount by default anchors requests to match from the start to the end, or not at all.
|
3717
3733
|
Rails solves this problem by using a `anchor: false` option in your routes.
|
3718
3734
|
In Grape this option can be used as well when a method is defined.
|
3719
3735
|
|
@@ -3729,12 +3745,44 @@ class TwitterAPI < Grape::API
|
|
3729
3745
|
end
|
3730
3746
|
```
|
3731
3747
|
|
3732
|
-
This will match all paths starting with '/statuses/'. There is one caveat though:
|
3733
|
-
the `
|
3734
|
-
|
3735
|
-
|
3736
|
-
|
3737
|
-
|
3748
|
+
This will match all paths starting with '/statuses/'. There is one caveat though: the `params[:status]` parameter only holds the first part of the request url.
|
3749
|
+
Luckily this can be circumvented by using the described above syntax for path specification and using the `PATH_INFO` Rack environment variable, using `env['PATH_INFO']`. This will hold everything that comes after the '/statuses/' part.
|
3750
|
+
|
3751
|
+
## Instance Variables
|
3752
|
+
|
3753
|
+
You can use instance variables to pass information across the various stages of a request. An instance variable set within a `before` validator is accessible within the endpoint's code and can also be utilized within the `rescue_from` handler.
|
3754
|
+
|
3755
|
+
```ruby
|
3756
|
+
class TwitterAPI < Grape::API
|
3757
|
+
before do
|
3758
|
+
@var = 1
|
3759
|
+
end
|
3760
|
+
|
3761
|
+
get '/' do
|
3762
|
+
puts @var # => 1
|
3763
|
+
raise
|
3764
|
+
end
|
3765
|
+
|
3766
|
+
rescue_from :all do
|
3767
|
+
puts @var # => 1
|
3768
|
+
end
|
3769
|
+
end
|
3770
|
+
```
|
3771
|
+
|
3772
|
+
The values of instance variables cannot be shared among various endpoints within the same API. This limitation arises due to Grape generating a new instance for each request made. Consequently, instance variables set within an endpoint during one request differ from those set during a subsequent request, as they exist within separate instances.
|
3773
|
+
|
3774
|
+
```ruby
|
3775
|
+
class TwitterAPI < Grape::API
|
3776
|
+
get '/first' do
|
3777
|
+
@var = 1
|
3778
|
+
puts @var # => 1
|
3779
|
+
end
|
3780
|
+
|
3781
|
+
get '/second' do
|
3782
|
+
puts @var # => nil
|
3783
|
+
end
|
3784
|
+
end
|
3785
|
+
```
|
3738
3786
|
|
3739
3787
|
## Using Custom Middleware
|
3740
3788
|
|
@@ -3943,8 +3991,7 @@ describe Twitter::API do
|
|
3943
3991
|
end
|
3944
3992
|
```
|
3945
3993
|
|
3946
|
-
In Rails, HTTP request tests would go into the `spec/requests` group. You may want your API code to go into
|
3947
|
-
`app/api` - you can match that layout under `spec` by adding the following in `spec/rails_helper.rb`.
|
3994
|
+
In Rails, HTTP request tests would go into the `spec/requests` group. You may want your API code to go into `app/api` - you can match that layout under `spec` by adding the following in `spec/rails_helper.rb`.
|
3948
3995
|
|
3949
3996
|
```ruby
|
3950
3997
|
RSpec.configure do |config|
|
@@ -3978,10 +4025,7 @@ end
|
|
3978
4025
|
|
3979
4026
|
### Stubbing Helpers
|
3980
4027
|
|
3981
|
-
Because helpers are mixed in based on the context when an endpoint is defined, it can
|
3982
|
-
be difficult to stub or mock them for testing. The `Grape::Endpoint.before_each` method
|
3983
|
-
can help by allowing you to define behavior on the endpoint that will run before every
|
3984
|
-
request.
|
4028
|
+
Because helpers are mixed in based on the context when an endpoint is defined, it can be difficult to stub or mock them for testing. The `Grape::Endpoint.before_each` method can help by allowing you to define behavior on the endpoint that will run before every request.
|
3985
4029
|
|
3986
4030
|
```ruby
|
3987
4031
|
describe 'an endpoint that needs helpers stubbed' do
|
@@ -4107,8 +4151,7 @@ Grape integrates with following third-party tools:
|
|
4107
4151
|
|
4108
4152
|
## Contributing to Grape
|
4109
4153
|
|
4110
|
-
Grape is work of hundreds of contributors. You're encouraged to submit pull requests, propose
|
4111
|
-
features and discuss issues.
|
4154
|
+
Grape is work of hundreds of contributors. You're encouraged to submit pull requests, propose features and discuss issues.
|
4112
4155
|
|
4113
4156
|
See [CONTRIBUTING](CONTRIBUTING.md).
|
4114
4157
|
|