diff --git a/CHANGELOG.md b/CHANGELOG.md index e5d85dfec9..7aea934087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ #### Fixes * [#2504](https://github.com/ruby-grape/grape/pull/2504): Fix leaky modules in specs - [@ericproulx](https://github.com/ericproulx). +* [#2506](https://github.com/ruby-grape/grape/pull/2506): Fix fetch_formatter api_format - [@ericproulx](https://github.com/ericproulx). * Your contribution here. ### 2.2.0 (2024-09-14) diff --git a/README.md b/README.md index 9d46af6515..0c6c56aff4 100644 --- a/README.md +++ b/README.md @@ -3066,7 +3066,7 @@ end * `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. * `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). -You can override this process explicitly by specifying `env['api.format']` in the API itself. +You can override this process explicitly by calling `api_format` in the API itself. For example, the following API will let you upload arbitrary files and return their contents as an attachment with the correct MIME type. ```ruby @@ -3074,7 +3074,7 @@ class Twitter::API < Grape::API post 'attachment' do filename = params[:file][:filename] content_type MIME::Types.type_for(filename)[0].to_s - env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is" + api_format :binary # there's no formatter for :binary, data will be returned "as is" header 'Content-Disposition', "attachment; filename*=UTF-8''#{CGI.escape(filename)}" params[:file][:tempfile].read end diff --git a/UPGRADING.md b/UPGRADING.md index ab80fe4cb4..0c6c54f6ed 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -3,6 +3,12 @@ Upgrading Grape ### Upgrading to >= 2.3.0 +### `content_type` vs `api.format` inside API + +Before 2.3.0, `content_type` had priority over `env['api.format']` when set in an API, which was incorrect. The priority has been flipped and `env['api.format']` will be checked first. +In addition, the function `api_format` has been added. Instead of setting `env['api.format']` directly, you can call `api_format`. +See [#2506](https://github.com/ruby-grape/grape/pull/2506) for more information. + #### Remove Deprecated Methods and Options - Deprecated `file` method has been removed. Use `send_file` or `stream`. diff --git a/lib/grape/dsl/inside_route.rb b/lib/grape/dsl/inside_route.rb index 72b365bc44..a7efcd4905 100644 --- a/lib/grape/dsl/inside_route.rb +++ b/lib/grape/dsl/inside_route.rb @@ -452,7 +452,11 @@ def entity_representation_for(entity_class, object, options) end def http_version - env['HTTP_VERSION'] || env[Rack::SERVER_PROTOCOL] + env.fetch(Grape::Http::Headers::HTTP_VERSION) { env[Rack::SERVER_PROTOCOL] } + end + + def api_format(format) + env[Grape::Env::API_FORMAT] = format end def context diff --git a/lib/grape/http/headers.rb b/lib/grape/http/headers.rb index ab8770ab4c..eb4d389152 100644 --- a/lib/grape/http/headers.rb +++ b/lib/grape/http/headers.rb @@ -6,6 +6,7 @@ module Headers HTTP_ACCEPT_VERSION = 'HTTP_ACCEPT_VERSION' HTTP_ACCEPT = 'HTTP_ACCEPT' HTTP_TRANSFER_ENCODING = 'HTTP_TRANSFER_ENCODING' + HTTP_VERSION = 'HTTP_VERSION' ALLOW = 'Allow' LOCATION = 'Location' diff --git a/lib/grape/middleware/formatter.rb b/lib/grape/middleware/formatter.rb index 4de1af02ea..5cb7614638 100644 --- a/lib/grape/middleware/formatter.rb +++ b/lib/grape/middleware/formatter.rb @@ -53,7 +53,7 @@ def build_formatted_response(status, headers, bodies) end def fetch_formatter(headers, options) - api_format = mime_types[headers[Rack::CONTENT_TYPE]] || env[Grape::Env::API_FORMAT] + api_format = env.fetch(Grape::Env::API_FORMAT) { mime_types[headers[Rack::CONTENT_TYPE]] } Grape::Formatter.formatter_for(api_format, options[:formatters]) end diff --git a/spec/grape/api_spec.rb b/spec/grape/api_spec.rb index 14e2c92579..bd63987807 100644 --- a/spec/grape/api_spec.rb +++ b/spec/grape/api_spec.rb @@ -4092,9 +4092,9 @@ def my_method expect(last_response.body).to eq({ meaning_of_life: 42 }.to_json) end - it 'can be overwritten with an explicit content type' do + it 'can be overwritten with an explicit api_format' do subject.get '/meaning_of_life_with_content_type' do - content_type 'text/plain' + api_format :txt { meaning_of_life: 42 }.to_s end get '/meaning_of_life_with_content_type'