Skip to content

Instantly share code, notes, and snippets.

@mudge
Last active March 24, 2023 05:27
Show Gist options
  • Select an option

  • Save mudge/acde31a5319726b9fdba419ffe7f5bcb to your computer and use it in GitHub Desktop.

Select an option

Save mudge/acde31a5319726b9fdba419ffe7f5bcb to your computer and use it in GitHub Desktop.

Revisions

  1. mudge revised this gist Nov 22, 2022. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions api_controller_spec.rb
    Original file line number Diff line number Diff line change
    @@ -4,14 +4,14 @@
    controller do
    def index
    respond_to do |format|
    format.plain { render plain: "Plain text" }
    format.text { render plain: "Plain text" }
    format.json { render json: "JSON" }
    end
    end

    def show
    respond_to do |format|
    format.plain { render plain: "Plain text" }
    format.text { render plain: "Plain text" }
    format.any { render plain: "Fallback" }
    end
    end
  2. mudge revised this gist Nov 19, 2022. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions api_controller_spec.rb
    Original file line number Diff line number Diff line change
    @@ -72,12 +72,12 @@ def show
    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(ActionDispatch::Http::MimeNegotiation::InvalidType)
    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(ActionDispatch::Http::MimeNegotiation::InvalidType)
    expect { get :show, params: { id: 1 } }.to raise_error(Mime::Type::InvalidMimeType)
    end
    end
  3. mudge revised this gist Nov 19, 2022. 1 changed file with 12 additions and 0 deletions.
    12 changes: 12 additions & 0 deletions api_controller_spec.rb
    Original file line number Diff line number Diff line change
    @@ -68,4 +68,16 @@ def show

    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(ActionDispatch::Http::MimeNegotiation::InvalidType)
    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(ActionDispatch::Http::MimeNegotiation::InvalidType)
    end
    end
  4. mudge revised this gist Nov 19, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion api_controller_spec.rb
    Original file line number Diff line number Diff line change
    @@ -64,7 +64,7 @@ def show
    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}
    get :show, params: { id: 1 }

    expect(response.body).to eq("Fallback")
    end
  5. mudge revised this gist Nov 19, 2022. 1 changed file with 15 additions and 0 deletions.
    15 changes: 15 additions & 0 deletions api_controller_spec.rb
    Original file line number Diff line number Diff line change
    @@ -8,6 +8,13 @@ def index
    format.json { render json: "JSON" }
    end
    end

    def show
    respond_to do |format|
    format.plain { render plain: "Plain text" }
    format.any { render plain: "Fallback" }
    end
    end
    end

    it "returns the first format when given no Accept header" do
    @@ -53,4 +60,12 @@ def index

    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
    end
  6. mudge revised this gist Nov 19, 2022. 2 changed files with 66 additions and 3 deletions.
    13 changes: 10 additions & 3 deletions api_controller.rb
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,8 @@
    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
    @@ -11,8 +11,15 @@ class ApiController < ApplicationController
    # 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)
    request.set_header(
    "action_dispatch.request.formats",
    requested_mime_types.select { |type| type.symbol || type.ref == "*/*" }
    )
    end

    def requested_mime_types
    56 changes: 56 additions & 0 deletions api_controller_spec.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,56 @@
    require "rails_helper"

    RSpec.describe ApiController do
    controller do
    def index
    respond_to do |format|
    format.plain { render plain: "Plain text" }
    format.json { render json: "JSON" }
    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
    end
  7. mudge created this gist Nov 19, 2022.
    21 changes: 21 additions & 0 deletions api_controller.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    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).
    def only_respect_accept_header
    request.set_header("action_dispatch.request.formats", requested_mime_types)
    end

    def requested_mime_types
    Mime::Type.parse(request.get_header("HTTP_ACCEPT").to_s).presence || [Mime::ALL]
    end
    end