Skip to content

Render a redirect message as the plain text it claims to be - #2845

Open
ericproulx wants to merge 1 commit into
masterfrom
fix/redirect-plain-text-body
Open

Render a redirect message as the plain text it claims to be#2845
ericproulx wants to merge 1 commit into
masterfrom
fix/redirect-plain-text-body

Conversation

@ericproulx

Copy link
Copy Markdown
Contributor

Summary

#redirect announces its message as text/plain — and has since it was introduced in 2015, in a commit literally titled "Redirect as plain text with optional message override" — but it only ever set the header. The body was still handed to the API's own formatter:

class API < Grape::API
  format :json
  get('/r') { redirect '/there' }
end
HTTP/1.1 302 Found
Location: /there
Content-Type: text/plain

"This resource has been moved temporarily to /there."

Quotes included. That is neither valid plain text nor what a client reading the content type would expect, and the header and body contradict each other.

API format before after
format :json text/plain + "…moved temporarily to /there." (quoted) text/plain + …moved temporarily to /there.
format :txt correct already unchanged
format :xml quoted/encoded by the XML formatter plain text

The existing #redirect specs missed it because they run on the default :txt format, where the formatter is a no-op.

Approach

Set api.format alongside the header — the same lever an endpoint already has via #api_format (spec'd at api_spec.rb:4494) — so the message is rendered by the txt formatter whatever the API declares. :txt is always resolvable: Grape::Formatter.formatter_for falls back to the built-in registry, which is not narrowed by the API's format.

It is per-request rack env, so other routes on the same API are unaffected — there is a spec pinning that.

Backward compatibility

No UPGRADING entry. The Content-Type was already text/plain; only the body changes, and only for APIs whose format is not :txt — from an encoded form that contradicted the header to the plain text it always claimed to be. Redirect bodies are informational; clients follow Location.

Longstanding rather than a regression: present since v0.14.0 (d1bba79d, 2015) and identical in 3.3.4.

Test plan

  • 3 new examples in endpoint_spec.rb under #redirect: plain-text body on a JSON API, an overridden body likewise, and a guard that other routes keep the API's format. Verified 2 of them fail without the lib/ change.
  • Full RSpec suite passes locally (2546 examples, 0 failures).
  • RuboCop clean.
  • CI green.

🤖 Generated with Claude Code

#redirect announces its message as text/plain and has done since it was
introduced in 2015 ("Redirect as plain text with optional message override"),
but it only set the header. The body was still handed to the API's own
formatter, so on a JSON API the sentence came back JSON-encoded:

    format :json
    get('/r') { redirect '/there' }

    Content-Type: text/plain
    "This resource has been moved temporarily to /there."

quotes included -- neither valid plain text nor something a client reading the
content type would expect. The existing specs missed it because they run on
the default :txt format, where the formatter is a no-op.

Set api.format alongside the header, the same lever an endpoint already has
via #api_format, so the message is rendered by the txt formatter whatever the
API declares. It is per-request env, so other routes on the same API are
untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ericproulx
ericproulx force-pushed the fix/redirect-plain-text-body branch from 63cf9a3 to 4e7d9d5 Compare August 1, 2026 11:46
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

Danger Report

No issues found.

View run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant