Last active
March 24, 2023 05:27
-
-
Save mudge/acde31a5319726b9fdba419ffe7f5bcb to your computer and use it in GitHub Desktop.
Instruct Rails to respect the Accept header even if it contains a wildcard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ApiController < ApplicationController | |
before_action :only_respect_accept_header | |
private | |
# By default, Rails will ignore the Accept header if it contains a wildcard | |
# and assume the client wants HTML (or JS if using XMLHttpRequest). See | |
# https://github.com/rails/rails/blob/a807a4f4f95798616a2a85856f77fdfc48da4832/actionpack/lib/action_dispatch/http/mime_negotiation.rb#L171-L173 | |
# | |
# If you don't expect your clients to be browsers, we want to override this | |
# and only set the request formats from the Accept header, falling back to | |
# the wildcard if it is not present (and therefore respecting our | |
# priorities). | |
# | |
# Note we have to filter down the MIME types to ones Rails understands | |
# otherwise it will raise an error when attempting to set formats for the | |
# lookup context. | |
def only_respect_accept_header | |
request.set_header( | |
"action_dispatch.request.formats", | |
requested_mime_types.select { |type| type.symbol || type.ref == "*/*" } | |
) | |
end | |
def requested_mime_types | |
Mime::Type.parse(request.get_header("HTTP_ACCEPT").to_s).presence || [Mime::ALL] | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "rails_helper" | |
RSpec.describe ApiController do | |
controller do | |
def index | |
respond_to do |format| | |
format.text { render plain: "Plain text" } | |
format.json { render json: "JSON" } | |
end | |
end | |
def show | |
respond_to do |format| | |
format.text { render plain: "Plain text" } | |
format.any { render plain: "Fallback" } | |
end | |
end | |
end | |
it "returns the first format when given no Accept header" do | |
get :index | |
expect(response.media_type).to eq("text/plain") | |
end | |
it "returns the first format when given a wildcard Accept header" do | |
request.headers["Accept"] = "*/*" | |
get :index | |
expect(response.media_type).to eq("text/plain") | |
end | |
it "returns the first format when given Safari and Chrome's default Accept header" do | |
request.headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8" | |
get :index | |
expect(response.media_type).to eq("text/plain") | |
end | |
it "returns a text/plain response when explicitly requested" do | |
request.headers["Accept"] = "text/plain" | |
get :index | |
expect(response.media_type).to eq("text/plain") | |
end | |
it "returns an application/json response when explicitly requested" do | |
request.headers["Accept"] = "application/json" | |
get :index | |
expect(response.media_type).to eq("application/json") | |
end | |
it "raises an error if given an unsupported MIME type" do | |
request.headers["Accept"] = "image/webp" | |
expect { get :index }.to raise_error(ActionController::UnknownFormat) | |
end | |
it "does not raise an error when given an unsupported MIME type if an 'any' format is present" do | |
request.headers["Accept"] = "image/webp" | |
get :show, params: { id: 1 } | |
expect(response.body).to eq("Fallback") | |
end | |
it "raises an error when given an invalid MIME type" do | |
request.headers["Accept"] = "not a valid MIME type" | |
expect { get :index }.to raise_error(Mime::Type::InvalidMimeType) | |
end | |
it "raises an error when given an invalid MIME type even if an 'any' format is present" do | |
request.headers["Accept"] = "not a valid MIME type" | |
expect { get :show, params: { id: 1 } }.to raise_error(Mime::Type::InvalidMimeType) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment