Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
* [#2827](https://github.com/ruby-grape/grape/pull/2827): Make the `cascade` DSL getter return the configured value (`cascade false` read back as `true`) - [@ericproulx](https://github.com/ericproulx).
* [#2829](https://github.com/ruby-grape/grape/pull/2829): Fix a cascading route handing over only to the last route registered for the path, making a middle version (3+ mounted versions with a catch-all) answer 406 - [@ericproulx](https://github.com/ericproulx).
* [#2826](https://github.com/ruby-grape/grape/pull/2826): Fix `api.version` not being set for the root route of a path-versioned API (`GET /v1`) - [@ericproulx](https://github.com/ericproulx).
* [#2845](https://github.com/ruby-grape/grape/pull/2845): Render a `redirect` message as the plain text its content type announces, instead of letting the API's formatter re-encode it - [@ericproulx](https://github.com/ericproulx).
* Your contribution here.

### 3.3.5 (2026-07-30)
Expand Down
5 changes: 5 additions & 0 deletions lib/grape/dsl/inside_route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ def redirect(url, permanent: false, body: nil)
body_message ||= "This resource has been moved temporarily to #{url}."
end
header 'Location', url
# The message is plain text, so say so and render it as such. Setting
# only the header left the body to the API's own formatter, which on a
# JSON API returned the sentence wrapped in quotes under a text/plain
# content type.
content_type 'text/plain'
api_format :txt
body body_message
end

Expand Down
32 changes: 32 additions & 0 deletions spec/grape/endpoint_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,38 @@ def handle_argument_error
get '/hey'
expect(last_response.body).to eq 'test body'
end

# The message is announced as text/plain, so it has to be rendered as such
# whatever the API's own format is. Left to the JSON formatter it came back
# as a quoted JSON string under a text/plain content type.
context 'when the API declares a format of its own' do
before do
subject.format :json
subject.get('/hey') { redirect '/ha' }
end

it 'renders the message as plain text' do
get '/hey'

expect(last_response.headers[Rack::CONTENT_TYPE]).to eq('text/plain')
expect(last_response.body).to eq 'This resource has been moved temporarily to /ha.'
end

it 'renders an overridden body as plain text too' do
subject.get('/there') { redirect '/ha', body: 'go away' }

get '/there'
expect(last_response.body).to eq 'go away'
end

it 'leaves the format of other routes alone' do
subject.get('/plain') { { a: 1 } }

get '/plain'
expect(last_response.headers[Rack::CONTENT_TYPE]).to eq('application/json')
expect(last_response.body).to eq({ a: 1 }.to_json)
end
end
end

describe 'NameError' do
Expand Down
Loading