You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Match Accept media types case-insensitively (#2847)
Media types are case-insensitive (RFC 9110 section 8.3.1), but the registered
ones are spelled in lower case and matched literally, so a differently-cased
Accept header found nothing:
Accept: TEXT/PLAIN -> served application/json
Accept: APPLICATION/VND.TWITTER-V1+JSON -> api.version nil
Neither failed loudly. Content negotiation fell through to the default
format, and header versioning behaved as though no version had been asked
for, so the request was served by whichever version matched first -- the
client quietly got something other than what it asked for.
Three sites decided this, all comparing against lower-case registered types:
the formatter's Accept lookup, MediaType.best_quality_media_type, and the
vendor pattern in MediaType.parse / .match?. Down-case the incoming media
type at each. The vendor pattern stays lower-case, which is the case a
vendor and version are declared in and therefore compared in.
Grape already treats media types this way when deciding whether to escape an
error body (Middleware::Error#html_content_type?, from #2789).
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,6 +56,7 @@
56
56
*[#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).
57
57
*[#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).
58
58
*[#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).
59
+
*[#2847](https://github.com/ruby-grape/grape/pull/2847): Match `Accept` media types case-insensitively, so a differently-cased header still negotiates the content type and resolves a vendor version - [@ericproulx](https://github.com/ericproulx).
59
60
*[#2834](https://github.com/ruby-grape/grape/pull/2834): Restore the #2824 fix for cascaded routes leaking `route_info` and path captures, silently reverted by #2829 - [@ericproulx](https://github.com/ericproulx).
60
61
*[#2838](https://github.com/ruby-grape/grape/pull/2838): Reject request params nested in more arrays than the block declares, instead of silently unwrapping them and passing validation, and report `type: Array[JSON]` errors against the element that failed - [@ericproulx](https://github.com/ericproulx).
61
62
*[#2842](https://github.com/ruby-grape/grape/pull/2842): Warn at definition time when a `rescue_from` class is already covered by one registered earlier in the same scope, since the later handler never runs - [@ericproulx](https://github.com/ericproulx).
0 commit comments