grape 2.4.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +351 -0
- data/CONTRIBUTING.md +18 -10
- data/README.md +457 -228
- data/UPGRADING.md +1240 -1
- data/grape.gemspec +7 -7
- data/lib/grape/api/instance.rb +65 -116
- data/lib/grape/api.rb +53 -62
- data/lib/grape/content_types.rb +58 -3
- data/lib/grape/cookies.rb +2 -6
- data/lib/grape/declared_params_handler.rb +116 -0
- data/lib/grape/dry_types.rb +48 -4
- data/lib/grape/dsl/callbacks.rb +8 -58
- data/lib/grape/dsl/declared.rb +35 -0
- data/lib/grape/dsl/desc.rb +24 -67
- data/lib/grape/dsl/entity.rb +106 -0
- data/lib/grape/dsl/helpers.rb +80 -65
- data/lib/grape/dsl/inside_route.rb +56 -296
- data/lib/grape/dsl/logger.rb +4 -9
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +86 -93
- data/lib/grape/dsl/request_response.rb +139 -139
- data/lib/grape/dsl/rescue_options.rb +25 -0
- data/lib/grape/dsl/routing.rb +328 -203
- data/lib/grape/dsl/settings.rb +29 -135
- data/lib/grape/dsl/validations.rb +38 -44
- data/lib/grape/dsl/version_options.rb +23 -0
- data/lib/grape/endpoint/options.rb +25 -0
- data/lib/grape/endpoint.rb +285 -245
- data/lib/grape/env.rb +8 -3
- data/lib/grape/error_formatter/base.rb +27 -21
- data/lib/grape/error_formatter/json.rb +6 -0
- data/lib/grape/error_formatter/txt.rb +10 -10
- data/lib/grape/error_formatter.rb +6 -2
- data/lib/grape/exceptions/base.rb +20 -44
- data/lib/grape/exceptions/error_response.rb +48 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/internal_server_error.rb +16 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_message_body.rb +1 -1
- data/lib/grape/exceptions/invalid_version_header.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/method_not_allowed.rb +1 -1
- data/lib/grape/exceptions/missing_group_type.rb +0 -2
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/request_error.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +1 -1
- data/lib/grape/exceptions/unknown_error_formatter.rb +11 -0
- data/lib/grape/exceptions/unknown_parameter.rb +1 -1
- data/lib/grape/exceptions/unknown_params_builder.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
- data/lib/grape/exceptions/validation.rb +25 -7
- data/lib/grape/exceptions/validation_array_errors.rb +4 -0
- data/lib/grape/exceptions/validation_errors.rb +21 -23
- data/lib/grape/formatter/json.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +6 -9
- data/lib/grape/json.rb +38 -2
- data/lib/grape/locale/en.yml +3 -5
- data/lib/grape/middleware/auth/base.rb +4 -3
- data/lib/grape/middleware/auth/dsl.rb +9 -23
- data/lib/grape/middleware/base.rb +41 -36
- data/lib/grape/middleware/error.rb +271 -73
- data/lib/grape/middleware/formatter.rb +152 -63
- data/lib/grape/middleware/precomputed_content_types.rb +51 -0
- data/lib/grape/middleware/stack.rb +20 -11
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -3
- data/lib/grape/middleware/versioner/base.rb +40 -48
- data/lib/grape/middleware/versioner/header.rb +5 -23
- data/lib/grape/middleware/versioner/param.rb +2 -2
- data/lib/grape/middleware/versioner/path.rb +54 -7
- data/lib/grape/mountable.rb +22 -0
- data/lib/grape/namespace.rb +19 -11
- data/lib/grape/params_builder/base.rb +2 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +1 -1
- data/lib/grape/params_builder.rb +2 -19
- data/lib/grape/parser/json.rb +1 -1
- data/lib/grape/path.rb +8 -72
- data/lib/grape/precompiled_json.rb +50 -0
- data/lib/grape/request.rb +57 -20
- data/lib/grape/router/base_route.rb +53 -7
- data/lib/grape/router/greedy_route.rb +16 -6
- data/lib/grape/router/mustermann_pattern.rb +44 -0
- data/lib/grape/router/pattern/path.rb +78 -0
- data/lib/grape/router/pattern.rb +65 -37
- data/lib/grape/router/route.rb +66 -18
- data/lib/grape/router.rb +126 -115
- data/lib/grape/serve_stream/file_body.rb +7 -0
- data/lib/grape/serve_stream/sendfile_response.rb +3 -5
- data/lib/grape/serve_stream/stream_response.rb +7 -0
- data/lib/grape/testing.rb +33 -0
- data/lib/grape/util/api_description.rb +67 -0
- data/lib/grape/util/cache.rb +22 -2
- data/lib/grape/util/deep_freeze.rb +34 -0
- data/lib/grape/util/freeze_on_new.rb +20 -0
- data/lib/grape/util/inheritable_setting.rb +777 -39
- data/lib/grape/util/lazy/base.rb +16 -0
- data/lib/grape/util/lazy/block.rb +2 -9
- data/lib/grape/util/lazy/value.rb +2 -9
- data/lib/grape/util/lazy/value_enumerable.rb +13 -16
- data/lib/grape/util/media_type.rb +13 -9
- data/lib/grape/util/path_normalizer.rb +37 -0
- data/lib/grape/util/registry.rb +13 -3
- data/lib/grape/util/shadowed_rescue_handlers.rb +49 -0
- data/lib/grape/util/stackable_values.rb +39 -10
- data/lib/grape/util/translation.rb +42 -0
- data/lib/grape/validations/attributes_iterator.rb +61 -29
- data/lib/grape/validations/coerce_options.rb +21 -0
- data/lib/grape/validations/contract_scope.rb +8 -13
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/oneof_collector.rb +35 -0
- data/lib/grape/validations/param_scope_tracker.rb +62 -0
- data/lib/grape/validations/params_documentation.rb +52 -0
- data/lib/grape/validations/params_scope.rb +217 -304
- data/lib/grape/validations/shared_options.rb +19 -0
- data/lib/grape/validations/single_attribute_iterator.rb +6 -4
- data/lib/grape/validations/types/array_coercer.rb +7 -10
- data/lib/grape/validations/types/custom_type_coercer.rb +47 -85
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
- data/lib/grape/validations/types/dry_type_coercer.rb +10 -15
- data/lib/grape/validations/types/json.rb +1 -3
- data/lib/grape/validations/types/multiple_type_coercer.rb +5 -3
- data/lib/grape/validations/types/primitive_coercer.rb +14 -33
- data/lib/grape/validations/types/set_coercer.rb +1 -1
- data/lib/grape/validations/types/variant_collection_coercer.rb +16 -3
- data/lib/grape/validations/types.rb +29 -51
- data/lib/grape/validations/validations_spec.rb +164 -0
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
- data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
- data/lib/grape/validations/validators/base.rb +118 -34
- data/lib/grape/validations/validators/coerce_validator.rb +25 -37
- data/lib/grape/validations/validators/contract_scope_validator.rb +10 -5
- data/lib/grape/validations/validators/default_validator.rb +13 -20
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
- data/lib/grape/validations/validators/except_values_validator.rb +14 -4
- data/lib/grape/validations/validators/length_validator.rb +23 -22
- data/lib/grape/validations/validators/multiple_params_base.rb +12 -9
- data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
- data/lib/grape/validations/validators/oneof_validator.rb +51 -0
- data/lib/grape/validations/validators/presence_validator.rb +4 -2
- data/lib/grape/validations/validators/regexp_validator.rb +11 -3
- data/lib/grape/validations/validators/same_as_validator.rb +7 -15
- data/lib/grape/validations/validators/values_validator.rb +34 -26
- data/lib/grape/version.rb +1 -2
- data/lib/grape/xml.rb +8 -1
- data/lib/grape.rb +58 -31
- metadata +62 -39
- data/lib/grape/api/helpers.rb +0 -9
- data/lib/grape/dsl/api.rb +0 -17
- data/lib/grape/dsl/configuration.rb +0 -15
- data/lib/grape/exceptions/conflicting_types.rb +0 -11
- data/lib/grape/exceptions/empty_message_body.rb +0 -11
- data/lib/grape/exceptions/invalid_parameters.rb +0 -11
- data/lib/grape/exceptions/missing_option.rb +0 -11
- data/lib/grape/exceptions/too_deep_parameters.rb +0 -11
- data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
- data/lib/grape/exceptions/unknown_options.rb +0 -11
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -24
- data/lib/grape/extensions/hash.rb +0 -27
- data/lib/grape/extensions/hashie/mash.rb +0 -24
- data/lib/grape/middleware/globals.rb +0 -14
- data/lib/grape/types/invalid_value.rb +0 -8
- data/lib/grape/util/base_inheritable.rb +0 -43
- data/lib/grape/util/inheritable_values.rb +0 -29
- data/lib/grape/util/reverse_stackable_values.rb +0 -15
- data/lib/grape/util/strict_hash_configuration.rb +0 -108
- data/lib/grape/validations/attributes_doc.rb +0 -60
- data/lib/grape/validations/validator_factory.rb +0 -15
data/README.md
CHANGED
|
@@ -4,160 +4,13 @@
|
|
|
4
4
|
[](https://github.com/ruby-grape/grape/actions/workflows/test.yml)
|
|
5
5
|
[](https://coveralls.io/github/ruby-grape/grape?branch=master)
|
|
6
6
|
|
|
7
|
-
## Table of Contents
|
|
8
|
-
|
|
9
|
-
- [What is Grape?](#what-is-grape)
|
|
10
|
-
- [Stable Release](#stable-release)
|
|
11
|
-
- [Project Resources](#project-resources)
|
|
12
|
-
- [Grape for Enterprise](#grape-for-enterprise)
|
|
13
|
-
- [Installation](#installation)
|
|
14
|
-
- [Basic Usage](#basic-usage)
|
|
15
|
-
- [Rails 7.1](#rails-71)
|
|
16
|
-
- [Mounting](#mounting)
|
|
17
|
-
- [All](#all)
|
|
18
|
-
- [Rack](#rack)
|
|
19
|
-
- [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
|
|
20
|
-
- [Rails](#rails)
|
|
21
|
-
- [Zeitwerk](#zeitwerk)
|
|
22
|
-
- [Modules](#modules)
|
|
23
|
-
- [Remounting](#remounting)
|
|
24
|
-
- [Mount Configuration](#mount-configuration)
|
|
25
|
-
- [Versioning](#versioning)
|
|
26
|
-
- [Strategies](#strategies)
|
|
27
|
-
- [Path](#path)
|
|
28
|
-
- [Header](#header)
|
|
29
|
-
- [Accept-Version Header](#accept-version-header)
|
|
30
|
-
- [Param](#param)
|
|
31
|
-
- [Linting](#linting)
|
|
32
|
-
- [Bug in Rack::ETag under Rack 3.X](#bug-in-racketag-under-rack-3x)
|
|
33
|
-
- [Describing Methods](#describing-methods)
|
|
34
|
-
- [Configuration](#configuration)
|
|
35
|
-
- [Parameters](#parameters)
|
|
36
|
-
- [Params Class](#params-class)
|
|
37
|
-
- [Declared](#declared)
|
|
38
|
-
- [Include Parent Namespaces](#include-parent-namespaces)
|
|
39
|
-
- [Include Missing](#include-missing)
|
|
40
|
-
- [Evaluate Given](#evaluate-given)
|
|
41
|
-
- [Parameter Precedence](#parameter-precedence)
|
|
42
|
-
- [Parameter Validation and Coercion](#parameter-validation-and-coercion)
|
|
43
|
-
- [Supported Parameter Types](#supported-parameter-types)
|
|
44
|
-
- [Integer/Fixnum and Coercions](#integerfixnum-and-coercions)
|
|
45
|
-
- [Custom Types and Coercions](#custom-types-and-coercions)
|
|
46
|
-
- [Multipart File Parameters](#multipart-file-parameters)
|
|
47
|
-
- [First-Class JSON Types](#first-class-json-types)
|
|
48
|
-
- [Multiple Allowed Types](#multiple-allowed-types)
|
|
49
|
-
- [Validation of Nested Parameters](#validation-of-nested-parameters)
|
|
50
|
-
- [Dependent Parameters](#dependent-parameters)
|
|
51
|
-
- [Group Options](#group-options)
|
|
52
|
-
- [Renaming](#renaming)
|
|
53
|
-
- [Built-in Validators](#built-in-validators)
|
|
54
|
-
- [allow_blank](#allow_blank)
|
|
55
|
-
- [values](#values)
|
|
56
|
-
- [except_values](#except_values)
|
|
57
|
-
- [same_as](#same_as)
|
|
58
|
-
- [length](#length)
|
|
59
|
-
- [regexp](#regexp)
|
|
60
|
-
- [mutually_exclusive](#mutually_exclusive)
|
|
61
|
-
- [exactly_one_of](#exactly_one_of)
|
|
62
|
-
- [at_least_one_of](#at_least_one_of)
|
|
63
|
-
- [all_or_none_of](#all_or_none_of)
|
|
64
|
-
- [Nested mutually_exclusive, exactly_one_of, at_least_one_of, all_or_none_of](#nested-mutually_exclusive-exactly_one_of-at_least_one_of-all_or_none_of)
|
|
65
|
-
- [Namespace Validation and Coercion](#namespace-validation-and-coercion)
|
|
66
|
-
- [Custom Validators](#custom-validators)
|
|
67
|
-
- [Validation Errors](#validation-errors)
|
|
68
|
-
- [I18n](#i18n)
|
|
69
|
-
- [Custom Validation messages](#custom-validation-messages)
|
|
70
|
-
- [presence, allow_blank, values, regexp](#presence-allow_blank-values-regexp)
|
|
71
|
-
- [same_as](#same_as-1)
|
|
72
|
-
- [length](#length-1)
|
|
73
|
-
- [all_or_none_of](#all_or_none_of-1)
|
|
74
|
-
- [mutually_exclusive](#mutually_exclusive-1)
|
|
75
|
-
- [exactly_one_of](#exactly_one_of-1)
|
|
76
|
-
- [at_least_one_of](#at_least_one_of-1)
|
|
77
|
-
- [Coerce](#coerce)
|
|
78
|
-
- [With Lambdas](#with-lambdas)
|
|
79
|
-
- [Pass symbols for i18n translations](#pass-symbols-for-i18n-translations)
|
|
80
|
-
- [Overriding Attribute Names](#overriding-attribute-names)
|
|
81
|
-
- [With Default](#with-default)
|
|
82
|
-
- [Using dry-validation or dry-schema](#using-dry-validation-or-dry-schema)
|
|
83
|
-
- [Headers](#headers)
|
|
84
|
-
- [Request](#request)
|
|
85
|
-
- [Header Case Handling](#header-case-handling)
|
|
86
|
-
- [Response](#response)
|
|
87
|
-
- [Routes](#routes)
|
|
88
|
-
- [Helpers](#helpers)
|
|
89
|
-
- [Path Helpers](#path-helpers)
|
|
90
|
-
- [Parameter Documentation](#parameter-documentation)
|
|
91
|
-
- [Cookies](#cookies)
|
|
92
|
-
- [HTTP Status Code](#http-status-code)
|
|
93
|
-
- [Redirecting](#redirecting)
|
|
94
|
-
- [Recognizing Path](#recognizing-path)
|
|
95
|
-
- [Allowed Methods](#allowed-methods)
|
|
96
|
-
- [Raising Exceptions](#raising-exceptions)
|
|
97
|
-
- [Default Error HTTP Status Code](#default-error-http-status-code)
|
|
98
|
-
- [Handling 404](#handling-404)
|
|
99
|
-
- [Exception Handling](#exception-handling)
|
|
100
|
-
- [Rescuing exceptions inside namespaces](#rescuing-exceptions-inside-namespaces)
|
|
101
|
-
- [Unrescuable Exceptions](#unrescuable-exceptions)
|
|
102
|
-
- [Exceptions that should be rescued explicitly](#exceptions-that-should-be-rescued-explicitly)
|
|
103
|
-
- [Logging](#logging)
|
|
104
|
-
- [API Formats](#api-formats)
|
|
105
|
-
- [JSONP](#jsonp)
|
|
106
|
-
- [CORS](#cors)
|
|
107
|
-
- [Content-type](#content-type)
|
|
108
|
-
- [API Data Formats](#api-data-formats)
|
|
109
|
-
- [JSON and XML Processors](#json-and-xml-processors)
|
|
110
|
-
- [RESTful Model Representations](#restful-model-representations)
|
|
111
|
-
- [Grape Entities](#grape-entities)
|
|
112
|
-
- [Hypermedia and Roar](#hypermedia-and-roar)
|
|
113
|
-
- [Rabl](#rabl)
|
|
114
|
-
- [Active Model Serializers](#active-model-serializers)
|
|
115
|
-
- [Sending Raw or No Data](#sending-raw-or-no-data)
|
|
116
|
-
- [Authentication](#authentication)
|
|
117
|
-
- [Basic Auth](#basic-auth)
|
|
118
|
-
- [Register custom middleware for authentication](#register-custom-middleware-for-authentication)
|
|
119
|
-
- [Describing and Inspecting an API](#describing-and-inspecting-an-api)
|
|
120
|
-
- [Current Route and Endpoint](#current-route-and-endpoint)
|
|
121
|
-
- [Before, After and Finally](#before-after-and-finally)
|
|
122
|
-
- [Anchoring](#anchoring)
|
|
123
|
-
- [Instance Variables](#instance-variables)
|
|
124
|
-
- [Using Custom Middleware](#using-custom-middleware)
|
|
125
|
-
- [Grape Middleware](#grape-middleware)
|
|
126
|
-
- [Rails Middleware](#rails-middleware)
|
|
127
|
-
- [Remote IP](#remote-ip)
|
|
128
|
-
- [Writing Tests](#writing-tests)
|
|
129
|
-
- [Writing Tests with Rack](#writing-tests-with-rack)
|
|
130
|
-
- [RSpec](#rspec)
|
|
131
|
-
- [Airborne](#airborne)
|
|
132
|
-
- [MiniTest](#minitest)
|
|
133
|
-
- [Writing Tests with Rails](#writing-tests-with-rails)
|
|
134
|
-
- [RSpec](#rspec-1)
|
|
135
|
-
- [MiniTest](#minitest-1)
|
|
136
|
-
- [Stubbing Helpers](#stubbing-helpers)
|
|
137
|
-
- [Reloading API Changes in Development](#reloading-api-changes-in-development)
|
|
138
|
-
- [Reloading in Rack Applications](#reloading-in-rack-applications)
|
|
139
|
-
- [Reloading in Rails Applications](#reloading-in-rails-applications)
|
|
140
|
-
- [Performance Monitoring](#performance-monitoring)
|
|
141
|
-
- [Active Support Instrumentation](#active-support-instrumentation)
|
|
142
|
-
- [endpoint_run.grape](#endpoint_rungrape)
|
|
143
|
-
- [endpoint_render.grape](#endpoint_rendergrape)
|
|
144
|
-
- [endpoint_run_filters.grape](#endpoint_run_filtersgrape)
|
|
145
|
-
- [endpoint_run_validators.grape](#endpoint_run_validatorsgrape)
|
|
146
|
-
- [format_response.grape](#format_responsegrape)
|
|
147
|
-
- [Monitoring Products](#monitoring-products)
|
|
148
|
-
- [Contributing to Grape](#contributing-to-grape)
|
|
149
|
-
- [Security](#security)
|
|
150
|
-
- [License](#license)
|
|
151
|
-
- [Copyright](#copyright)
|
|
152
|
-
|
|
153
7
|
## What is Grape?
|
|
154
8
|
|
|
155
9
|
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.
|
|
156
10
|
|
|
157
11
|
## Stable Release
|
|
158
12
|
|
|
159
|
-
You're reading the documentation for the stable release of Grape,
|
|
160
|
-
Please read [UPGRADING](https://github.com/ruby-grape/grape/blob/v2.4.0/UPGRADING.md) when upgrading from a previous version.
|
|
13
|
+
You're reading the documentation for the stable release of Grape, 4.0.1.
|
|
161
14
|
|
|
162
15
|
## Project Resources
|
|
163
16
|
|
|
@@ -174,7 +27,7 @@ The maintainers of Grape are working with Tidelift to deliver commercial support
|
|
|
174
27
|
|
|
175
28
|
## Installation
|
|
176
29
|
|
|
177
|
-
Ruby
|
|
30
|
+
Ruby 3.3 or newer is required.
|
|
178
31
|
|
|
179
32
|
Grape is available as a gem, to install it run:
|
|
180
33
|
|
|
@@ -458,7 +311,7 @@ mount ::Some::Api => '/some/api', with: { condition: true }
|
|
|
458
311
|
|
|
459
312
|
You can access `configuration` on the class (to use as dynamic attributes), inside blocks (like namespace)
|
|
460
313
|
|
|
461
|
-
If you want logic happening
|
|
314
|
+
If you want logic happening based on a `configuration`, you can use the helper `given`.
|
|
462
315
|
|
|
463
316
|
```ruby
|
|
464
317
|
class ConditionalEndpoint::API < Grape::API
|
|
@@ -609,7 +462,7 @@ vnd.vendor-and-or-resource-v1234+format
|
|
|
609
462
|
|
|
610
463
|
Basically all tokens between the final `-` and the `+` will be interpreted as the version.
|
|
611
464
|
|
|
612
|
-
Using this versioning strategy, clients should pass the desired version in the HTTP `Accept`
|
|
465
|
+
Using this versioning strategy, clients should pass the desired version in the HTTP `Accept` header.
|
|
613
466
|
|
|
614
467
|
curl -H Accept:application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
|
|
615
468
|
|
|
@@ -631,7 +484,7 @@ Using this versioning strategy, clients should pass the desired version in the H
|
|
|
631
484
|
|
|
632
485
|
curl -H "Accept-Version:v1" http://localhost:9292/statuses/public_timeline
|
|
633
486
|
|
|
634
|
-
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.
|
|
487
|
+
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-Version` 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.
|
|
635
488
|
|
|
636
489
|
#### Param
|
|
637
490
|
|
|
@@ -671,13 +524,13 @@ Grape.config.lint = true
|
|
|
671
524
|
```
|
|
672
525
|
|
|
673
526
|
### Bug in Rack::ETag under Rack 3.X
|
|
674
|
-
If you're using Rack 3.X and the `Rack::
|
|
527
|
+
If you're using Rack 3.X and the `Rack::ETag` middleware (used by [Rails](https://guides.rubyonrails.org/rails_on_rack.html#inspecting-middleware-stack)), a [bug](https://github.com/rack/rack/pull/2324) related to linting has been fixed in [3.1.13](https://github.com/rack/rack/blob/v3.1.13/CHANGELOG.md#3113---2025-04-13) and [3.0.15](https://github.com/rack/rack/blob/v3.1.13/CHANGELOG.md#3015---2025-04-13) respectively.
|
|
675
528
|
|
|
676
529
|
## Describing Methods
|
|
677
530
|
|
|
678
531
|
You can add a description to API methods and namespaces. The description would be used by [grape-swagger][grape-swagger] to generate swagger compliant documentation.
|
|
679
532
|
|
|
680
|
-
Note: Description block is only for documentation and won't
|
|
533
|
+
Note: Description block is only for documentation and won't affect API behavior.
|
|
681
534
|
|
|
682
535
|
```ruby
|
|
683
536
|
desc 'Returns your public timeline.' do
|
|
@@ -713,7 +566,7 @@ end
|
|
|
713
566
|
* `params`: Define parameters directly from an `Entity`
|
|
714
567
|
* `success`: (former entity) The `Entity` to be used to present the success response for this route.
|
|
715
568
|
* `failure`: (former http_codes) A definition of the used failure HTTP Codes and Entities.
|
|
716
|
-
* `
|
|
569
|
+
* `default_response`: (former `default`) The definition and `Entity` used to present the default response for this route.
|
|
717
570
|
* `named`: A helper to give a route a name and find it with this name in the documentation Hash
|
|
718
571
|
* `headers`: A definition of the used Headers
|
|
719
572
|
* Other options can be found in [grape-swagger][grape-swagger]
|
|
@@ -726,6 +579,7 @@ Use `Grape.configure` to set up global settings at load time.
|
|
|
726
579
|
Currently the configurable settings are:
|
|
727
580
|
|
|
728
581
|
* `param_builder`: Sets the [Parameter Builder](#parameters), defaults to `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder`.
|
|
582
|
+
* `raise_rendering_errors`: Lets an error response that cannot be rendered propagate out of the middleware stack instead of being answered with a failsafe `500`, defaults to `false`. See [When the error response itself cannot be rendered](#when-the-error-response-itself-cannot-be-rendered).
|
|
729
583
|
|
|
730
584
|
To change a setting value make sure that at some point during load time the following code runs
|
|
731
585
|
|
|
@@ -766,7 +620,7 @@ get :public_timeline do
|
|
|
766
620
|
end
|
|
767
621
|
```
|
|
768
622
|
|
|
769
|
-
Parameters are automatically populated from the request body on `POST` and `
|
|
623
|
+
Parameters are automatically populated from the request body on `POST`, `PUT` and `QUERY` for form input, JSON and XML content-types.
|
|
770
624
|
|
|
771
625
|
The request:
|
|
772
626
|
|
|
@@ -1329,28 +1183,6 @@ The following are all valid types, supported out of the box by Grape:
|
|
|
1329
1183
|
* Rack::Multipart::UploadedFile (alias `File`)
|
|
1330
1184
|
* JSON
|
|
1331
1185
|
|
|
1332
|
-
### Integer/Fixnum and Coercions
|
|
1333
|
-
|
|
1334
|
-
Please be aware that the behavior differs between Ruby 2.4 and earlier versions.
|
|
1335
|
-
In Ruby 2.4, values consisting of numbers are converted to Integer, but in earlier versions it will be treated as Fixnum.
|
|
1336
|
-
|
|
1337
|
-
```ruby
|
|
1338
|
-
params do
|
|
1339
|
-
requires :integers, type: Hash do
|
|
1340
|
-
requires :int, coerce: Integer
|
|
1341
|
-
end
|
|
1342
|
-
end
|
|
1343
|
-
get '/int' do
|
|
1344
|
-
params[:integers][:int].class
|
|
1345
|
-
end
|
|
1346
|
-
|
|
1347
|
-
...
|
|
1348
|
-
|
|
1349
|
-
get '/int' integers: { int: '45' }
|
|
1350
|
-
#=> Integer in ruby 2.4
|
|
1351
|
-
#=> Fixnum in earlier ruby versions
|
|
1352
|
-
```
|
|
1353
|
-
|
|
1354
1186
|
### Custom Types and Coercions
|
|
1355
1187
|
|
|
1356
1188
|
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.
|
|
@@ -1503,6 +1335,56 @@ end
|
|
|
1503
1335
|
client.get('/', status_codes: %w(1 two)) # => [1, "two"]
|
|
1504
1336
|
```
|
|
1505
1337
|
|
|
1338
|
+
### Multiple Hash Schemas with `oneof`
|
|
1339
|
+
|
|
1340
|
+
A Hash parameter that may take one of several different shapes can be declared with the `oneof:` option. Each variant is a `Proc` that uses the normal `params` DSL — so the full validator surface (`requires`, `optional`, `regexp:`, `values:`, `allow_blank:`, nested `Hash`/`Array`, etc.) is available inside.
|
|
1341
|
+
|
|
1342
|
+
```ruby
|
|
1343
|
+
params do
|
|
1344
|
+
requires :value, type: Hash, oneof: [
|
|
1345
|
+
proc { requires :fixed_price, type: Float },
|
|
1346
|
+
proc do
|
|
1347
|
+
requires :time_unit, type: String
|
|
1348
|
+
requires :rate, type: Float
|
|
1349
|
+
end
|
|
1350
|
+
]
|
|
1351
|
+
end
|
|
1352
|
+
post '/pricing' do
|
|
1353
|
+
params[:value]
|
|
1354
|
+
end
|
|
1355
|
+
```
|
|
1356
|
+
|
|
1357
|
+
Both of these requests succeed:
|
|
1358
|
+
|
|
1359
|
+
```bash
|
|
1360
|
+
curl -d '{"value":{"fixed_price":100.0}}' -H 'Content-Type: application/json' /pricing
|
|
1361
|
+
curl -d '{"value":{"time_unit":"hour","rate":50.0}}' -H 'Content-Type: application/json' /pricing
|
|
1362
|
+
```
|
|
1363
|
+
|
|
1364
|
+
A request that doesn't match any variant returns `400` with `value does not match any of the allowed schemas`.
|
|
1365
|
+
|
|
1366
|
+
Variants are tried in declaration order; the first variant that validates without errors wins, and any coercions it performed (e.g. `"150.5"` → `150.5`) are applied to the request params. Nested hash structures inside variants work the same as elsewhere in Grape:
|
|
1367
|
+
|
|
1368
|
+
```ruby
|
|
1369
|
+
params do
|
|
1370
|
+
requires :options, type: Hash, oneof: [
|
|
1371
|
+
proc do
|
|
1372
|
+
requires :form, type: Hash do
|
|
1373
|
+
requires :colour, type: String
|
|
1374
|
+
optional :size, type: Integer
|
|
1375
|
+
end
|
|
1376
|
+
end,
|
|
1377
|
+
proc do
|
|
1378
|
+
requires :api, type: Hash do
|
|
1379
|
+
requires :authenticated, type: Grape::API::Boolean
|
|
1380
|
+
end
|
|
1381
|
+
end
|
|
1382
|
+
]
|
|
1383
|
+
end
|
|
1384
|
+
```
|
|
1385
|
+
|
|
1386
|
+
`oneof:` requires `type: Hash`. The variants array must be non-empty and each entry must be a `Proc`; violating either raises an `ArgumentError` at definition time.
|
|
1387
|
+
|
|
1506
1388
|
### Validation of Nested Parameters
|
|
1507
1389
|
|
|
1508
1390
|
Parameters can be nested using `group` or by calling `requires` or `optional` with a block.
|
|
@@ -1589,7 +1471,7 @@ params do
|
|
|
1589
1471
|
end
|
|
1590
1472
|
```
|
|
1591
1473
|
|
|
1592
|
-
You can organize settings into layers using nested `with
|
|
1474
|
+
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.
|
|
1593
1475
|
|
|
1594
1476
|
```ruby
|
|
1595
1477
|
params do
|
|
@@ -1677,6 +1559,27 @@ params do
|
|
|
1677
1559
|
end
|
|
1678
1560
|
```
|
|
1679
1561
|
|
|
1562
|
+
An exclusive upper bound is expressed with the standard Ruby `...` range (three dots). Ruby has no range syntax for an exclusive *lower* bound; an arity-one `:values` Proc predicate (see below) is the recommended way to express one, though excluding a specific endpoint (e.g. zero) via `:except_values` also works:
|
|
1563
|
+
|
|
1564
|
+
```ruby
|
|
1565
|
+
params do
|
|
1566
|
+
requires :score, type: Float, values: 0.0...5.0 # 0.0 <= score < 5.0
|
|
1567
|
+
requires :positive, type: Float, values: ->(v) { v > 0.0 } # amount > 0.0 (recommended)
|
|
1568
|
+
requires :other, type: Float, values: 0.0.., except_values: [0.0] # amount > 0.0 (alternative)
|
|
1569
|
+
end
|
|
1570
|
+
```
|
|
1571
|
+
|
|
1572
|
+
Combined with `:type`, ranges (open or closed) cover common numeric bound checks without a dedicated validator:
|
|
1573
|
+
|
|
1574
|
+
```ruby
|
|
1575
|
+
params do
|
|
1576
|
+
requires :quantity, type: Integer, values: 1.. # must be a positive integer
|
|
1577
|
+
requires :discount, type: Float, values: 0.0..100.0 # must be between 0 and 100
|
|
1578
|
+
requires :rating, type: Integer, values: 5..5 # must be exactly 5
|
|
1579
|
+
requires :numbers, type: [Integer], values: 1.. # every element must be positive
|
|
1580
|
+
end
|
|
1581
|
+
```
|
|
1582
|
+
|
|
1680
1583
|
The `:values` option can also be supplied with a `Proc`, evaluated lazily with each request.
|
|
1681
1584
|
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.
|
|
1682
1585
|
|
|
@@ -1698,24 +1601,27 @@ end
|
|
|
1698
1601
|
|
|
1699
1602
|
While Procs are convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
|
|
1700
1603
|
|
|
1701
|
-
|
|
1604
|
+
When using `optional` together with `:values`, a missing key, a `nil` value, and any value that coerces to `nil` (such as `""` for `type: Symbol`) all pass validation — `optional` collapses "key may be absent" with "value may be nil". To reject blank values while still allowing the key to be absent, add `allow_blank: false`:
|
|
1702
1605
|
|
|
1703
1606
|
```ruby
|
|
1704
1607
|
params do
|
|
1705
|
-
|
|
1608
|
+
optional :state, type: Symbol, values: [:active, :inactive], allow_blank: false
|
|
1706
1609
|
end
|
|
1707
1610
|
```
|
|
1708
1611
|
|
|
1612
|
+
With `requires`, blank values are already rejected: `requires` enforces presence and `:values` rejects `nil`.
|
|
1613
|
+
|
|
1709
1614
|
#### `except_values`
|
|
1710
1615
|
|
|
1711
1616
|
Parameters can be restricted from having a specific set of values with the `:except_values` option.
|
|
1712
1617
|
|
|
1713
|
-
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.
|
|
1618
|
+
The `except_values` validator behaves similarly to the `values` validator in that it accepts either an Array, a Range (including open/beginless/endless ranges), or a Proc. Unlike the `values` validator, however, `except_values` only accepts Procs with arity zero.
|
|
1714
1619
|
|
|
1715
1620
|
```ruby
|
|
1716
1621
|
params do
|
|
1717
1622
|
requires :browser, except_values: [ 'ie6', 'ie7', 'ie8' ]
|
|
1718
1623
|
requires :port, except_values: { value: 0..1024, message: 'is not allowed' }
|
|
1624
|
+
requires :negative, type: Integer, except_values: ..-1
|
|
1719
1625
|
requires :hashtag, except_values: -> { Hashtag.FORBIDDEN_LIST }
|
|
1720
1626
|
end
|
|
1721
1627
|
```
|
|
@@ -1920,9 +1826,9 @@ end
|
|
|
1920
1826
|
```ruby
|
|
1921
1827
|
class AlphaNumeric < Grape::Validations::Validators::Base
|
|
1922
1828
|
def validate_param!(attr_name, params)
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1829
|
+
return if params[attr_name].match?(/\A[[:alnum:]]+\z/)
|
|
1830
|
+
|
|
1831
|
+
validation_error!(attr_name, 'must consist of alpha-numeric characters')
|
|
1926
1832
|
end
|
|
1927
1833
|
end
|
|
1928
1834
|
```
|
|
@@ -1938,9 +1844,9 @@ You can also create custom classes that take parameters.
|
|
|
1938
1844
|
```ruby
|
|
1939
1845
|
class Length < Grape::Validations::Validators::Base
|
|
1940
1846
|
def validate_param!(attr_name, params)
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1847
|
+
return if params[attr_name].length <= @options
|
|
1848
|
+
|
|
1849
|
+
validation_error!(attr_name, "must be at the most #{@options} characters long")
|
|
1944
1850
|
end
|
|
1945
1851
|
end
|
|
1946
1852
|
```
|
|
@@ -1962,10 +1868,10 @@ class Admin < Grape::Validations::Validators::Base
|
|
|
1962
1868
|
# @attrs being [:admin_field] and once with @attrs being [:admin_false_field]
|
|
1963
1869
|
return unless request.params.key?(@attrs.first)
|
|
1964
1870
|
# check if admin flag is set to true
|
|
1965
|
-
return unless @
|
|
1871
|
+
return unless @options
|
|
1966
1872
|
# check if user is admin or not
|
|
1967
1873
|
# as an example get a token from request and check if it's admin or not
|
|
1968
|
-
|
|
1874
|
+
validation_error!(@attrs, 'Can not set admin-only field.') unless request.headers['X-Access-Token'] == 'admin'
|
|
1969
1875
|
end
|
|
1970
1876
|
end
|
|
1971
1877
|
```
|
|
@@ -1980,7 +1886,50 @@ params do
|
|
|
1980
1886
|
end
|
|
1981
1887
|
```
|
|
1982
1888
|
|
|
1983
|
-
|
|
1889
|
+
Each validator is instantiated once at route definition time and frozen. Any setup (option parsing, message building) should happen in `initialize`, not in `validate_param!` or `validate`.
|
|
1890
|
+
|
|
1891
|
+
#### Available helpers
|
|
1892
|
+
|
|
1893
|
+
The following protected/private helpers are available in any `Grape::Validations::Validators::Base` subclass:
|
|
1894
|
+
|
|
1895
|
+
| Helper | Description |
|
|
1896
|
+
|---|---|
|
|
1897
|
+
| `default_message_key(key)` | Class-level macro. Declares the default I18n key for `validation_error!`. A per-option `:message` override still takes precedence. |
|
|
1898
|
+
| `validation_error!(attr_name_or_params, message = @exception_message)` | Raises `Grape::Exceptions::Validation`. Accepts a single attribute name or a pre-computed array of full param names. |
|
|
1899
|
+
| `@options` | The validator option value, deep-frozen at initialization. |
|
|
1900
|
+
| `@attrs` | Frozen array of attribute names this validator applies to. |
|
|
1901
|
+
| `@scope` | The `ParamsScope` — use `@scope.full_name(attr_name)` for the fully-qualified param name. |
|
|
1902
|
+
| `option_value` | Returns `@options[:value]` if present, otherwise `@options`. |
|
|
1903
|
+
| `options_key?(key)` | Returns true if `@options` is a hash with a non-nil `key`. |
|
|
1904
|
+
| `hash_like?(obj)` | Returns true if `obj` responds to `key?`. |
|
|
1905
|
+
| `scrub(value)` | Returns `value` with invalid byte sequences scrubbed. |
|
|
1906
|
+
| `translate(key, **opts)` | I18n lookup with `:en` fallback and `grape.errors.messages` scope. Called at request time to respect per-request locale. |
|
|
1907
|
+
|
|
1908
|
+
Use `default_message_key` for a fixed I18n key. The message is resolved once at route definition time via `message`, so a per-option `:message` override still wins:
|
|
1909
|
+
|
|
1910
|
+
```ruby
|
|
1911
|
+
class SpecialValidator < Grape::Validations::Validators::Base
|
|
1912
|
+
default_message_key :special
|
|
1913
|
+
|
|
1914
|
+
def validate_param!(attr_name, params)
|
|
1915
|
+
return if valid?(params[attr_name])
|
|
1916
|
+
|
|
1917
|
+
validation_error!(attr_name)
|
|
1918
|
+
end
|
|
1919
|
+
end
|
|
1920
|
+
```
|
|
1921
|
+
|
|
1922
|
+
For interpolated messages that must respect per-request locale, call `translate` directly inside `validate_param!`:
|
|
1923
|
+
|
|
1924
|
+
```ruby
|
|
1925
|
+
class SpecialValidator < Grape::Validations::Validators::Base
|
|
1926
|
+
def validate_param!(attr_name, params)
|
|
1927
|
+
return if valid?(params[attr_name])
|
|
1928
|
+
|
|
1929
|
+
validation_error!(attr_name, translate(:special, min: 2, max: 10))
|
|
1930
|
+
end
|
|
1931
|
+
end
|
|
1932
|
+
```
|
|
1984
1933
|
|
|
1985
1934
|
### Validation Errors
|
|
1986
1935
|
|
|
@@ -2023,8 +1972,8 @@ To skip all subsequent validation checks when a specific param is found invalid,
|
|
|
2023
1972
|
The following example will not check if `:wine` is present unless it finds `:beer`.
|
|
2024
1973
|
```ruby
|
|
2025
1974
|
params do
|
|
2026
|
-
|
|
2027
|
-
|
|
1975
|
+
requires :beer, fail_fast: true
|
|
1976
|
+
requires :wine
|
|
2028
1977
|
end
|
|
2029
1978
|
```
|
|
2030
1979
|
The result of empty params would be a single `Grape::Exceptions::ValidationErrors` error.
|
|
@@ -2032,7 +1981,7 @@ The result of empty params would be a single `Grape::Exceptions::ValidationError
|
|
|
2032
1981
|
Similarly, no regular expression test will be performed if `:blah` is blank in the following example.
|
|
2033
1982
|
```ruby
|
|
2034
1983
|
params do
|
|
2035
|
-
|
|
1984
|
+
requires :blah, allow_blank: false, regexp: /blah/, fail_fast: true
|
|
2036
1985
|
end
|
|
2037
1986
|
```
|
|
2038
1987
|
|
|
@@ -2042,6 +1991,28 @@ Grape supports I18n for parameter-related error messages, but will fallback to E
|
|
|
2042
1991
|
|
|
2043
1992
|
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.
|
|
2044
1993
|
|
|
1994
|
+
Custom validators that inherit from `Grape::Validations::Validators::Base` have access to a `translate` helper (see `Grape::Util::Translation`) and should use it instead of calling `I18n` directly. It applies the same `:en` fallback as built-in validators, defaults `scope` to `'grape.errors.messages'`, and handles interpolation without needing `format`:
|
|
1995
|
+
|
|
1996
|
+
```ruby
|
|
1997
|
+
# Good — scope defaults to 'grape.errors.messages', interpolation forwarded automatically
|
|
1998
|
+
translate(:special, min: 2, max: 10)
|
|
1999
|
+
|
|
2000
|
+
# Bad — format is unnecessary and risks conflicting with I18n reserved keys
|
|
2001
|
+
format I18n.t(:special, scope: 'grape.errors.messages'), min: 2, max: 10
|
|
2002
|
+
```
|
|
2003
|
+
|
|
2004
|
+
Example custom validator using an interpolated i18n message:
|
|
2005
|
+
|
|
2006
|
+
```ruby
|
|
2007
|
+
class SpecialValidator < Grape::Validations::Validators::Base
|
|
2008
|
+
def validate_param!(attr_name, params)
|
|
2009
|
+
return if valid?(params[attr_name])
|
|
2010
|
+
|
|
2011
|
+
validation_error!(attr_name, translate(:special, min: 2, max: 10))
|
|
2012
|
+
end
|
|
2013
|
+
end
|
|
2014
|
+
```
|
|
2015
|
+
|
|
2045
2016
|
### Custom Validation messages
|
|
2046
2017
|
|
|
2047
2018
|
Grape supports custom validation messages for parameter-related and coerce-related error messages.
|
|
@@ -2330,6 +2301,78 @@ namespace :outer, requirements: { id: /[0-9]*/ } do
|
|
|
2330
2301
|
end
|
|
2331
2302
|
```
|
|
2332
2303
|
|
|
2304
|
+
A requirement can also name one of the capture types of [Mustermann](https://github.com/sinatra/mustermann), the pattern library Grape routes with, instead of a regular expression. Besides constraining the match, a capture type that carries a converter hands the endpoint the converted value rather than a String.
|
|
2305
|
+
|
|
2306
|
+
```ruby
|
|
2307
|
+
get ':id', requirements: { id: Integer } do
|
|
2308
|
+
params[:id] # => 23, an Integer, for GET /23 — GET /michael is a 404
|
|
2309
|
+
end
|
|
2310
|
+
|
|
2311
|
+
get 'events/:on', requirements: { on: :date } do
|
|
2312
|
+
params[:on] # => #<Date: 2020-01-02>, for GET /events/2020-01-02
|
|
2313
|
+
end
|
|
2314
|
+
```
|
|
2315
|
+
|
|
2316
|
+
`Integer`, `Float`, `Symbol`, `Date` and `Gem::Version` convert, as do their symbol spellings `:integer`, `:float`, `:symbol`, `:date` and `:version`. `:uuid`, `:slug` and `:locale` constrain the match without converting. A name Mustermann has no capture type for raises when the API is defined.
|
|
2317
|
+
|
|
2318
|
+
A `params` block still owns what the endpoint sees, since validation runs after the router: declaring `requires :id, type: String` alongside `requirements: { id: Integer }` hands the endpoint `"23"`.
|
|
2319
|
+
|
|
2320
|
+
`route_param` names the parameter itself, so it takes the constraint on its own — `route_param :id, requirements: Integer` — and refuses a Hash, which would name the parameter a second time. A namespace or an endpoint can carry several captures, so requirements there are the Hash form keyed by param name.
|
|
2321
|
+
|
|
2322
|
+
`route_param :id` defines the parameter on its own — `params[:id]` reaches the endpoint, and the route documents it as a path capture. `type:` and `requirements:` are two ways of typing it, and they work at different layers. `type: Integer` declares `requires :id, type: Integer`: the value is coerced, appears in `declared(params)` and carries its type into the route's documented params, and a value the path let through but the type rejects is a 400. `requirements: Integer` types the route instead: a segment that does not match is not this route at all, so it is a 404, and the value arrives already converted by the router, undeclared and documented only as a capture. A declared `type: Integer` also narrows the path, but to digits alone, so `/-7` is a 404 there while the requirement matches it. Given both, the requirement decides what the route matches and the declared type decides what the endpoint sees.
|
|
2323
|
+
|
|
2324
|
+
#### Dots in a path segment
|
|
2325
|
+
|
|
2326
|
+
A path capture matches `[^/?#.]+`: a dot introduces the format suffix, so a captured segment never contains one. A dotted value therefore either fails to route or arrives truncated, and the declared type makes no difference — `type: String` and `type: Float` compile the same pattern as an untyped parameter, only `type: Integer` narrows it to digits.
|
|
2327
|
+
|
|
2328
|
+
```ruby
|
|
2329
|
+
class WithFormat < Grape::API
|
|
2330
|
+
format :json
|
|
2331
|
+
route_param :name do
|
|
2332
|
+
get { params[:name] } # GET /report.pdf => 404, the segment cannot hold the dot
|
|
2333
|
+
end
|
|
2334
|
+
end
|
|
2335
|
+
|
|
2336
|
+
class WithoutFormat < Grape::API
|
|
2337
|
+
route_param :name do
|
|
2338
|
+
# GET /report.pdf => 200, params[:name] is "report" and params[:format] is "pdf"
|
|
2339
|
+
get { params[:name] }
|
|
2340
|
+
end
|
|
2341
|
+
end
|
|
2342
|
+
```
|
|
2343
|
+
|
|
2344
|
+
The second one is the one to watch: the extension is taken as the format, so the endpoint is handed a truncated value and the client still gets a 200.
|
|
2345
|
+
|
|
2346
|
+
Percent-encoding the dot routes it through — `GET /report%2Epdf` hands the endpoint `"report.pdf"` — and so does a requirement that admits dots:
|
|
2347
|
+
|
|
2348
|
+
```ruby
|
|
2349
|
+
route_param :name, requirements: /.+/ do
|
|
2350
|
+
get { params[:name] } # => "report.pdf" for GET /report.pdf, "1.2.3" for GET /1.2.3
|
|
2351
|
+
end
|
|
2352
|
+
```
|
|
2353
|
+
|
|
2354
|
+
Such a regexp claims the extension as well, so `GET /a.b.json` hands the endpoint `"a.b.json"` and format-by-extension no longer applies to that route. A narrower constraint leaves the suffix alone: `get ':n', requirements: { n: Float }` matches `/4.2` and still reads `.json` as the format.
|
|
2355
|
+
|
|
2356
|
+
### The QUERY Method
|
|
2357
|
+
|
|
2358
|
+
Grape supports [`QUERY`](https://www.rfc-editor.org/info/rfc10008/), a safe and idempotent method that carries its query in the request content rather than in the URI. It is the method to reach for when a query is too large, too structured, or too sensitive to encode into a query string.
|
|
2359
|
+
|
|
2360
|
+
```ruby
|
|
2361
|
+
params do
|
|
2362
|
+
requires :q, type: String
|
|
2363
|
+
optional :limit, type: Integer
|
|
2364
|
+
end
|
|
2365
|
+
query '/search' do
|
|
2366
|
+
Status.search(params[:q], limit: params[:limit])
|
|
2367
|
+
end
|
|
2368
|
+
```
|
|
2369
|
+
|
|
2370
|
+
```
|
|
2371
|
+
curl -X QUERY 'http://localhost:9292/search' -H Content-Type:application/json -d '{"q": "grape", "limit": 10}'
|
|
2372
|
+
```
|
|
2373
|
+
|
|
2374
|
+
The content is parsed into `params` exactly as it is for `POST`, and a successful response defaults to `200`, not `201`. Because the content *is* the query, a `QUERY` request that arrives without a `Content-Type` is rejected with `400` instead of falling back to the API's default format.
|
|
2375
|
+
|
|
2333
2376
|
## Helpers
|
|
2334
2377
|
|
|
2335
2378
|
You can define helper methods that your endpoints can use with the `helpers` macro by either giving a block or an array of modules.
|
|
@@ -2679,7 +2722,7 @@ You can set additional headers for the response. They will be merged with header
|
|
|
2679
2722
|
error!('Something went wrong', 500, 'X-Error-Detail' => 'Invalid token.')
|
|
2680
2723
|
```
|
|
2681
2724
|
|
|
2682
|
-
You can present documented errors with a Grape entity using the
|
|
2725
|
+
You can present documented errors with a Grape entity using the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
|
|
2683
2726
|
|
|
2684
2727
|
```ruby
|
|
2685
2728
|
module API
|
|
@@ -2766,6 +2809,19 @@ rescue_from :grape_exceptions do |e|
|
|
|
2766
2809
|
end
|
|
2767
2810
|
```
|
|
2768
2811
|
|
|
2812
|
+
The opt-in takes precedence over a catch-all handler, whether that catch-all is written as `rescue_from :all` or as a class such as `rescue_from StandardError`. So a validation failure stays a `400` rather than becoming whatever the catch-all returns:
|
|
2813
|
+
|
|
2814
|
+
```ruby
|
|
2815
|
+
class Twitter::API < Grape::API
|
|
2816
|
+
rescue_from StandardError do
|
|
2817
|
+
error!('server error', 500)
|
|
2818
|
+
end
|
|
2819
|
+
rescue_from :grape_exceptions # validation errors still answer 400
|
|
2820
|
+
end
|
|
2821
|
+
```
|
|
2822
|
+
|
|
2823
|
+
A handler registered for a specific Grape exception class is more precise than the opt-in and still wins, so `rescue_from Grape::Exceptions::ValidationErrors` keeps its own handler. The opt-in only outranks handlers that matched through a non-Grape ancestor.
|
|
2824
|
+
|
|
2769
2825
|
You can also rescue specific exceptions.
|
|
2770
2826
|
|
|
2771
2827
|
```ruby
|
|
@@ -2776,6 +2832,29 @@ end
|
|
|
2776
2832
|
|
|
2777
2833
|
In this case ```UserDefinedError``` must be inherited from ```StandardError```.
|
|
2778
2834
|
|
|
2835
|
+
When several classes could match, the one registered **first** in a scope wins — as with the clauses of a Ruby `rescue`. Register the more specific class before the broader one, or the narrower handler never runs:
|
|
2836
|
+
|
|
2837
|
+
```ruby
|
|
2838
|
+
class Twitter::API < Grape::API
|
|
2839
|
+
rescue_from ArgumentError do ... end # matched first
|
|
2840
|
+
rescue_from StandardError do ... end # everything else
|
|
2841
|
+
end
|
|
2842
|
+
```
|
|
2843
|
+
|
|
2844
|
+
Grape warns when a `rescue_from` is registered for a class an earlier one in the same scope already covers. This is about ordering within a scope; a handler in a nested namespace or a mounted API always takes precedence over one inherited from an enclosing scope, whatever the classes are.
|
|
2845
|
+
|
|
2846
|
+
A `rescue_from` also has to be declared **above** the routes it should cover. A route captures the handlers registered before it, so one declared below a route never runs for it:
|
|
2847
|
+
|
|
2848
|
+
```ruby
|
|
2849
|
+
class Twitter::API < Grape::API
|
|
2850
|
+
rescue_from :all do ... end # covers GET /statuses
|
|
2851
|
+
get :statuses do ... end
|
|
2852
|
+
rescue_from ArgumentError do ... end # never runs for GET /statuses
|
|
2853
|
+
end
|
|
2854
|
+
```
|
|
2855
|
+
|
|
2856
|
+
The same goes for `use`, `helpers` and the `before`/`after` callbacks.
|
|
2857
|
+
|
|
2779
2858
|
Notice that you could combine these two approaches (rescuing custom errors takes precedence). For example, it's useful for handling all exceptions except Grape validation errors.
|
|
2780
2859
|
|
|
2781
2860
|
```ruby
|
|
@@ -2790,12 +2869,12 @@ end
|
|
|
2790
2869
|
|
|
2791
2870
|
The error format will match the request format. See "Content-Types" below.
|
|
2792
2871
|
|
|
2793
|
-
Custom error formatters for existing and additional types can be defined with a proc.
|
|
2872
|
+
Custom error formatters for existing and additional types can be defined with a proc. The formatter receives a `Grape::Exceptions::ErrorResponse` value object as `error:` plus three context kwargs — `env:`, `include_backtrace:`, `include_original_exception:`. Pull just the keys you need with `**` to ignore the rest:
|
|
2794
2873
|
|
|
2795
2874
|
```ruby
|
|
2796
2875
|
class Twitter::API < Grape::API
|
|
2797
|
-
error_formatter :txt, ->(
|
|
2798
|
-
"error: #{message} from #{backtrace}"
|
|
2876
|
+
error_formatter :txt, ->(error:, **) {
|
|
2877
|
+
"error #{error.status}: #{error.message} from #{error.backtrace}"
|
|
2799
2878
|
}
|
|
2800
2879
|
end
|
|
2801
2880
|
```
|
|
@@ -2804,8 +2883,8 @@ You can also use a module or class.
|
|
|
2804
2883
|
|
|
2805
2884
|
```ruby
|
|
2806
2885
|
module CustomFormatter
|
|
2807
|
-
def self.call(
|
|
2808
|
-
{ message: message, backtrace: backtrace }
|
|
2886
|
+
def self.call(error:, **)
|
|
2887
|
+
{ status: error.status, message: error.message, backtrace: error.backtrace }
|
|
2809
2888
|
end
|
|
2810
2889
|
end
|
|
2811
2890
|
|
|
@@ -2835,6 +2914,43 @@ class Twitter::API < Grape::API
|
|
|
2835
2914
|
end
|
|
2836
2915
|
```
|
|
2837
2916
|
|
|
2917
|
+
#### Re-raising from inside a `rescue_from` block
|
|
2918
|
+
|
|
2919
|
+
A `rescue_from` block can re-raise an exception to invoke a different handler. This is useful for translating one exception class into another:
|
|
2920
|
+
|
|
2921
|
+
```ruby
|
|
2922
|
+
class Twitter::API < Grape::API
|
|
2923
|
+
rescue_from Grape::Exceptions::ValidationErrors do |e|
|
|
2924
|
+
raise Api::Exceptions::InvalidValueError, e.full_messages
|
|
2925
|
+
end
|
|
2926
|
+
|
|
2927
|
+
rescue_from Api::Exceptions::InvalidValueError do |e|
|
|
2928
|
+
error!({ errors: e.message }, 422)
|
|
2929
|
+
end
|
|
2930
|
+
end
|
|
2931
|
+
```
|
|
2932
|
+
|
|
2933
|
+
The first handler re-raises; the second handler runs against the new exception.
|
|
2934
|
+
|
|
2935
|
+
If the re-raised exception has no registered `rescue_from` and is a `Grape::Exceptions::Base` subclass, it is rendered through the default Grape error path (using its own `status` and `message`). Anything else — typos, `NoMethodError`, an unrelated `StandardError` — is treated as an internal error: it is exposed on the rack env for upstream Rack middleware to observe, and rendered to the API consumer as a generic `500 Internal Server Error`. This avoids leaking internal detail in the response body.
|
|
2936
|
+
|
|
2937
|
+
Because the exception is answered rather than raised, nothing above Grape catches it, so it is published under `rack.exception` — the key error trackers read for a handled exception — as well as Grape's own `grape.exception`. Grape does no logging of its own here; register a `rescue_from :internal_grape_exceptions` handler to take that over.
|
|
2938
|
+
|
|
2939
|
+
You can take control of the internal-error path by opting in with `rescue_from :internal_grape_exceptions`:
|
|
2940
|
+
|
|
2941
|
+
```ruby
|
|
2942
|
+
class Twitter::API < Grape::API
|
|
2943
|
+
rescue_from :internal_grape_exceptions do |e|
|
|
2944
|
+
Sentry.capture_exception(e)
|
|
2945
|
+
error!({ message: 'Something went wrong' }, 500)
|
|
2946
|
+
end
|
|
2947
|
+
end
|
|
2948
|
+
```
|
|
2949
|
+
|
|
2950
|
+
When this handler is registered the framework hands the original exception to you, and you own the response shape and any logging. The framework deliberately does not log internal errors itself — it has no way to know your preferred format or destination.
|
|
2951
|
+
|
|
2952
|
+
A second raise inside the redispatched handler is not redispatched again — it goes straight to the framework's generic 500. This bounds the chain at one redispatch and prevents loops.
|
|
2953
|
+
|
|
2838
2954
|
You can also rescue all exceptions with a code block and handle the Rack response at the lowest level.
|
|
2839
2955
|
|
|
2840
2956
|
```ruby
|
|
@@ -2974,6 +3090,38 @@ Any exception that is not subclass of `StandardError` should be rescued explicit
|
|
|
2974
3090
|
Usually it is not a case for an application logic as such errors point to problems in Ruby runtime.
|
|
2975
3091
|
This is following [standard recommendations for exceptions handling](https://ruby-doc.org/core/Exception.html).
|
|
2976
3092
|
|
|
3093
|
+
#### When the error response itself cannot be rendered
|
|
3094
|
+
|
|
3095
|
+
A `rescue_from` handler can build a payload its error formatter cannot serialize. The common case is echoing request-derived text back to the client:
|
|
3096
|
+
|
|
3097
|
+
```ruby
|
|
3098
|
+
class Missing < StandardError; end
|
|
3099
|
+
|
|
3100
|
+
class API < Grape::API
|
|
3101
|
+
format :json
|
|
3102
|
+
|
|
3103
|
+
rescue_from(Missing) { |e| error!({ error: 'not_found', detail: e.message }, 404) }
|
|
3104
|
+
|
|
3105
|
+
route_param(:id) { get { raise Missing, "no such thing: #{params[:id]}" } }
|
|
3106
|
+
end
|
|
3107
|
+
```
|
|
3108
|
+
|
|
3109
|
+
`GET /%C3%28` puts an invalid UTF-8 byte in `params[:id]`, and the JSON formatter raises when it reaches that byte in the message — after the handler has already returned, so `rescue_from :all` does not help.
|
|
3110
|
+
|
|
3111
|
+
Rather than let that exception escape the middleware stack, Grape answers `500`: first retrying the API's own format with the framework's `Internal Server Error` message, then falling back to a bare `text/plain` body if even that cannot be rendered.
|
|
3112
|
+
|
|
3113
|
+
The exception is published on the rack env under `rack.exception` — the key error trackers read to find an exception that was handled rather than raised — and on Grape's own `grape.exception`. It is also written to `rack.errors`, so it reaches the log with no tracker installed.
|
|
3114
|
+
|
|
3115
|
+
To opt out and have the exception propagate out of the middleware stack instead:
|
|
3116
|
+
|
|
3117
|
+
```ruby
|
|
3118
|
+
Grape.configure do |config|
|
|
3119
|
+
config.raise_rendering_errors = true
|
|
3120
|
+
end
|
|
3121
|
+
```
|
|
3122
|
+
|
|
3123
|
+
With this on the failsafe never runs: the exception is re-raised untouched, so neither env key is set and nothing is written to `rack.errors`.
|
|
3124
|
+
|
|
2977
3125
|
## Logging
|
|
2978
3126
|
|
|
2979
3127
|
`Grape::API` provides a `logger` method which by default will return an instance of the `Logger` class from Ruby's standard library.
|
|
@@ -3182,6 +3330,41 @@ Built-in formatters are the following.
|
|
|
3182
3330
|
* `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`
|
|
3183
3331
|
* `:binary`: data will be returned "as is"
|
|
3184
3332
|
|
|
3333
|
+
#### Serving pre-rendered JSON
|
|
3334
|
+
|
|
3335
|
+
The `:json` and `:serializable_hash` formatters encode whatever the endpoint returned,
|
|
3336
|
+
so a String that already holds JSON gets encoded a second time. Wrap the body in
|
|
3337
|
+
`Grape::PrecompiledJson` to opt that response out of encoding.
|
|
3338
|
+
|
|
3339
|
+
```ruby
|
|
3340
|
+
class API < Grape::API
|
|
3341
|
+
format :json
|
|
3342
|
+
|
|
3343
|
+
get '/cached' do
|
|
3344
|
+
body Grape::PrecompiledJson.new(Rails.cache.read('payload')) # => '{"id":1}', served as-is
|
|
3345
|
+
end
|
|
3346
|
+
end
|
|
3347
|
+
```
|
|
3348
|
+
|
|
3349
|
+
An `Array` is joined into a JSON array without its members being parsed, so a cached
|
|
3350
|
+
collection is spliced together rather than round-tripped. Members may themselves be
|
|
3351
|
+
`Grape::PrecompiledJson` instances.
|
|
3352
|
+
|
|
3353
|
+
```ruby
|
|
3354
|
+
Grape::PrecompiledJson.new(['{"id":1}', '{"id":2}']).to_s # => '[{"id":1},{"id":2}]'
|
|
3355
|
+
```
|
|
3356
|
+
|
|
3357
|
+
Bodies that are not wrapped keep encoding exactly as before, so this is opt-in per
|
|
3358
|
+
response rather than a mode the whole API is in. Wrap only the whole body: a wrapper
|
|
3359
|
+
nested inside a Hash or Array that is then handed to an encoder is serialized as an
|
|
3360
|
+
ordinary object, because no encoder knows to unwrap it. A value that is neither a
|
|
3361
|
+
String nor an Array raises `Grape::Exceptions::InvalidFormatter` and answers 500,
|
|
3362
|
+
rather than handing Rack a body it cannot serve. Nothing checks that the String
|
|
3363
|
+
actually holds JSON — that is the caller's responsibility.
|
|
3364
|
+
|
|
3365
|
+
Errors are unaffected: they are rendered by the error formatter, so `error!` still
|
|
3366
|
+
returns properly encoded JSON.
|
|
3367
|
+
|
|
3185
3368
|
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.
|
|
3186
3369
|
|
|
3187
3370
|
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.
|
|
@@ -3782,13 +3965,13 @@ class TwitterAPI < Grape::API
|
|
|
3782
3965
|
@var = 1
|
|
3783
3966
|
end
|
|
3784
3967
|
|
|
3785
|
-
|
|
3968
|
+
rescue_from :all do
|
|
3786
3969
|
puts @var # => 1
|
|
3787
|
-
raise
|
|
3788
3970
|
end
|
|
3789
3971
|
|
|
3790
|
-
|
|
3972
|
+
get '/' do
|
|
3791
3973
|
puts @var # => 1
|
|
3974
|
+
raise
|
|
3792
3975
|
end
|
|
3793
3976
|
end
|
|
3794
3977
|
```
|
|
@@ -4049,7 +4232,15 @@ end
|
|
|
4049
4232
|
|
|
4050
4233
|
### Stubbing Helpers
|
|
4051
4234
|
|
|
4052
|
-
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.
|
|
4235
|
+
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. `Grape::Endpoint.before_each` allows you to define behavior on the endpoint that will run before every request.
|
|
4236
|
+
|
|
4237
|
+
This feature is provided by `Grape::Testing`, a standalone module intended for test environments only. Add it to your test helper:
|
|
4238
|
+
|
|
4239
|
+
```ruby
|
|
4240
|
+
require 'grape/testing'
|
|
4241
|
+
```
|
|
4242
|
+
|
|
4243
|
+
Then use it in your tests:
|
|
4053
4244
|
|
|
4054
4245
|
```ruby
|
|
4055
4246
|
describe 'an endpoint that needs helpers stubbed' do
|
|
@@ -4060,7 +4251,7 @@ describe 'an endpoint that needs helpers stubbed' do
|
|
|
4060
4251
|
end
|
|
4061
4252
|
|
|
4062
4253
|
after do
|
|
4063
|
-
Grape::Endpoint.
|
|
4254
|
+
Grape::Endpoint.reset_before_each
|
|
4064
4255
|
end
|
|
4065
4256
|
|
|
4066
4257
|
it 'stubs the helper' do
|
|
@@ -4077,6 +4268,25 @@ Use [grape-reload](https://github.com/AlexYankee/grape-reload).
|
|
|
4077
4268
|
|
|
4078
4269
|
### Reloading in Rails Applications
|
|
4079
4270
|
|
|
4271
|
+
#### Rails 7+ (Zeitwerk)
|
|
4272
|
+
|
|
4273
|
+
Rails 7+ uses [Zeitwerk](https://github.com/fxn/zeitwerk) as the default autoloader, which automatically handles reloading of code in development mode without any additional configuration.
|
|
4274
|
+
|
|
4275
|
+
If your API files are in `app/api`, Zeitwerk will automatically autoload and reload them. No additional configuration is needed.
|
|
4276
|
+
|
|
4277
|
+
If you encounter issues with reloading, ensure that:
|
|
4278
|
+
|
|
4279
|
+
1. Your API files follow Zeitwerk naming conventions (file names should match class names).
|
|
4280
|
+
2. The `config.enable_reloading` is set to `true` in `config/environments/development.rb` (this is the default).
|
|
4281
|
+
|
|
4282
|
+
For troubleshooting autoloading issues, have a look at the [Rails documentation](https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#troubleshooting).
|
|
4283
|
+
|
|
4284
|
+
See the [Rails Autoloading and Reloading Constants guide](https://guides.rubyonrails.org/autoloading_and_reloading_constants.html) for more information.
|
|
4285
|
+
|
|
4286
|
+
#### Rails 6 and Earlier
|
|
4287
|
+
|
|
4288
|
+
For Rails versions before 7, you need to configure reloading manually.
|
|
4289
|
+
|
|
4080
4290
|
Add API paths to `config/application.rb`.
|
|
4081
4291
|
|
|
4082
4292
|
```ruby
|
|
@@ -4095,28 +4305,12 @@ if Rails.env.development?
|
|
|
4095
4305
|
api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
|
|
4096
4306
|
Rails.application.reload_routes!
|
|
4097
4307
|
end
|
|
4098
|
-
|
|
4308
|
+
ActiveSupport::Reloader.to_prepare do
|
|
4099
4309
|
api_reloader.execute_if_updated
|
|
4100
4310
|
end
|
|
4101
4311
|
end
|
|
4102
4312
|
```
|
|
4103
4313
|
|
|
4104
|
-
For Rails >= 5.1.4, change this:
|
|
4105
|
-
|
|
4106
|
-
```ruby
|
|
4107
|
-
ActionDispatch::Callbacks.to_prepare do
|
|
4108
|
-
api_reloader.execute_if_updated
|
|
4109
|
-
end
|
|
4110
|
-
```
|
|
4111
|
-
|
|
4112
|
-
to this:
|
|
4113
|
-
|
|
4114
|
-
```ruby
|
|
4115
|
-
ActiveSupport::Reloader.to_prepare do
|
|
4116
|
-
api_reloader.execute_if_updated
|
|
4117
|
-
end
|
|
4118
|
-
```
|
|
4119
|
-
|
|
4120
4314
|
See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-rails-3-reload-lib-directory-for-each-request/4368838#4368838) for more information.
|
|
4121
4315
|
|
|
4122
4316
|
## Performance Monitoring
|
|
@@ -4125,27 +4319,30 @@ See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-
|
|
|
4125
4319
|
|
|
4126
4320
|
Grape has built-in support for [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) which provides simple hook points to instrument key parts of your application.
|
|
4127
4321
|
|
|
4128
|
-
The following are currently supported:
|
|
4129
4322
|
|
|
4130
|
-
####
|
|
4323
|
+
#### Hook Points
|
|
4324
|
+
|
|
4325
|
+
The following hook points are currently supported:
|
|
4326
|
+
|
|
4327
|
+
##### endpoint_run.grape
|
|
4131
4328
|
|
|
4132
4329
|
The main execution of an endpoint, includes filters and rendering.
|
|
4133
4330
|
|
|
4134
4331
|
* *endpoint* - The endpoint instance
|
|
4135
4332
|
|
|
4136
|
-
|
|
4333
|
+
##### endpoint_render.grape
|
|
4137
4334
|
|
|
4138
4335
|
The execution of the main content block of the endpoint.
|
|
4139
4336
|
|
|
4140
4337
|
* *endpoint* - The endpoint instance
|
|
4141
4338
|
|
|
4142
|
-
|
|
4339
|
+
##### endpoint_run_filters.grape
|
|
4143
4340
|
|
|
4144
4341
|
* *endpoint* - The endpoint instance
|
|
4145
4342
|
* *filters* - The filters being executed
|
|
4146
4343
|
* *type* - The type of filters (before, before_validation, after_validation, after)
|
|
4147
4344
|
|
|
4148
|
-
|
|
4345
|
+
##### endpoint_run_validators.grape
|
|
4149
4346
|
|
|
4150
4347
|
The execution of validators.
|
|
4151
4348
|
|
|
@@ -4153,7 +4350,7 @@ The execution of validators.
|
|
|
4153
4350
|
* *validators* - The validators being executed
|
|
4154
4351
|
* *request* - The request being validated
|
|
4155
4352
|
|
|
4156
|
-
|
|
4353
|
+
##### format_response.grape
|
|
4157
4354
|
|
|
4158
4355
|
Serialization or template rendering.
|
|
4159
4356
|
|
|
@@ -4162,12 +4359,44 @@ Serialization or template rendering.
|
|
|
4162
4359
|
|
|
4163
4360
|
See the [ActiveSupport::Notifications documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) for information on how to subscribe to these events.
|
|
4164
4361
|
|
|
4362
|
+
#### Subscribe to Hooks
|
|
4363
|
+
|
|
4364
|
+
Once subscribed to the instrumentation, you can intercept the events reported above.
|
|
4365
|
+
|
|
4366
|
+
```ruby
|
|
4367
|
+
ActiveSupport::Notifications.subscribe(/<api_path>/) do |name, start, finish, id, payload|
|
|
4368
|
+
# your code to intercept the notification
|
|
4369
|
+
end
|
|
4370
|
+
```
|
|
4371
|
+
|
|
4372
|
+
The request data, the API’s internal data, and the response can be retrieved from the payload.
|
|
4373
|
+
|
|
4374
|
+
You can use `payload.fetch(:endpoint)` or directly `payload[:endpoint]`.
|
|
4375
|
+
|
|
4376
|
+
The `:endpoint` contains the data currently being processed, and access to attributes such as `body`, `request`, `params`, `headers`, `cookies` and `response_cookies`
|
|
4377
|
+
|
|
4378
|
+
For example, `payload[:endpoint].body` provides the current state of the response.
|
|
4379
|
+
|
|
4380
|
+
```ruby
|
|
4381
|
+
ActiveSupport::Notifications.subscribe(/v1/) do |name, start, finish, id, payload|
|
|
4382
|
+
hook_record = {
|
|
4383
|
+
hook: name
|
|
4384
|
+
status: payload[:env]&.dig("api.endpoint")&.status
|
|
4385
|
+
format: payload[:env]&.dig("api.format")
|
|
4386
|
+
body: payload[:endpoint]&.body
|
|
4387
|
+
duration: (finish - start) * 1000
|
|
4388
|
+
}
|
|
4389
|
+
# your code to save the notification
|
|
4390
|
+
end
|
|
4391
|
+
```
|
|
4392
|
+
|
|
4165
4393
|
### Monitoring Products
|
|
4166
4394
|
|
|
4167
4395
|
Grape integrates with following third-party tools:
|
|
4168
4396
|
|
|
4169
4397
|
* **New Relic** - [built-in support](https://docs.newrelic.com/docs/agents/ruby-agent/frameworks/grape-instrumentation) from v3.10.0 of the official [newrelic_rpm](https://github.com/newrelic/rpm) gem, also [newrelic-grape](https://github.com/xinminlabs/newrelic-grape) gem
|
|
4170
4398
|
* **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
|
|
4399
|
+
* **Rails Performance** - [rails_performance](https://github.com/igorkasyanchuk/rails_performance) gem
|
|
4171
4400
|
* **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
|
|
4172
4401
|
* **[AppSignal](https://www.appsignal.com)** - [appsignal-ruby](https://github.com/appsignal/appsignal-ruby) gem, [documentation](http://docs.appsignal.com/getting-started/supported-frameworks.html#grape)
|
|
4173
4402
|
* **[ElasticAPM](https://www.elastic.co/products/apm)** - [elastic-apm](https://github.com/elastic/apm-agent-ruby) gem, [documentation](https://www.elastic.co/guide/en/apm/agent/ruby/3.x/getting-started-rack.html#getting-started-grape)
|