Skip to content
Merged
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
39 changes: 39 additions & 0 deletions lib/jsonapi/rails/filter_media_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'rack/media_type'

module JSONAPI
module Rails
class FilterMediaType
JSONAPI_MEDIA_TYPE = 'application/vnd.api+json'.freeze

def initialize(app)
@app = app
end

def call(env)
return [415, {}, []] unless valid_content_type?(env['CONTENT_TYPE'])
return [406, {}, []] unless valid_accept?(env['HTTP_ACCEPT'])

@app.call(env)
end

private

def valid_content_type?(content_type)
Rack::MediaType.type(content_type) != JSONAPI_MEDIA_TYPE ||
Rack::MediaType.params(content_type) == {}
end

def valid_accept?(accept)
return true if accept.nil?

jsonapi_media_types =
accept.split(',')
.map(&:strip)
.select { |m| Rack::MediaType.type(m) == JSONAPI_MEDIA_TYPE }

jsonapi_media_types.empty? ||
jsonapi_media_types.any? { |m| Rack::MediaType.params(m) == {} }
end
end
end
end
7 changes: 5 additions & 2 deletions lib/jsonapi/rails/railtie.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'rails/railtie'

require 'jsonapi/rails/filter_media_type'
require 'jsonapi/rails/log_subscriber'
require 'jsonapi/rails/renderer'

Expand All @@ -19,14 +20,16 @@ class Railtie < ::Rails::Railtie
jsonapi_errors: ErrorsRenderer.new
}.freeze

initializer 'jsonapi-rails.init' do
initializer 'jsonapi-rails.init' do |app|
register_mime_type
register_parameter_parser
register_renderers
ActiveSupport.on_load(:action_controller) do
require 'jsonapi/rails/controller'
include ::JSONAPI::Rails::Controller
end

app.middleware.use FilterMediaType
end

private
Expand All @@ -49,7 +52,7 @@ def register_renderers
RENDERERS.each do |name, renderer|
::ActionController::Renderers.add(name) do |resources, options|
# Renderer proc is evaluated in the controller context.
self.content_type ||= Mime[:jsonapi]
headers['Content-Type'] = Mime[:jsonapi].to_s

ActiveSupport::Notifications.instrument('render.jsonapi-rails',
resources: resources,
Expand Down
17 changes: 17 additions & 0 deletions spec/content_negotiation_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require 'rails_helper'

describe ActionController::Base, type: :controller do
controller do
def index
render jsonapi: nil
end
end

context 'when sending data' do
it 'responds with application/vnd.api+json' do
get :index

expect(response.headers['Content-Type']).to eq('application/vnd.api+json')
end
end
end
69 changes: 69 additions & 0 deletions spec/filter_media_type_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
require 'rails_helper'

describe JSONAPI::Rails::FilterMediaType do
let(:app) { ->(_) { [200, {}, ['OK']] } }

context 'when not receiving JSON API Content-Type' do
it 'passes through' do
env = { 'CONTENT_TYPE' => 'application/json' }

expect(described_class.new(app).call(env)[0]).to eq(200)
end
end

context 'when receiving JSON API Content-Type without media parameters' do
it 'passes through' do
env = { 'CONTENT_TYPE' => 'application/vnd.api+json' }

expect(described_class.new(app).call(env)[0]).to eq(200)
end
end

context 'when receiving Content-Type with media parameters' do
it 'fails with 415 Unsupported Media Type' do
env = { 'CONTENT_TYPE' => 'application/vnd.api+json; charset=utf-8' }

expect(described_class.new(app).call(env)[0]).to eq(415)
end
end

context 'when not receiving JSON API in Accept' do
it 'passes through' do
env = { 'HTTP_ACCEPT' => 'application/json' }

expect(described_class.new(app).call(env)[0]).to eq(200)
end
end

context 'when receiving JSON API in Accept without media parameters' do
it 'passes through' do
env = { 'HTTP_ACCEPT' => 'application/vnd.api+json' }

expect(described_class.new(app).call(env)[0]).to eq(200)
end
end

context 'when receiving JSON API in Accept without media parameters among others' do
it 'passes through' do
env = { 'HTTP_ACCEPT' => 'application/json, application/vnd.api+json' }

expect(described_class.new(app).call(env)[0]).to eq(200)
end
end

context 'when receiving JSON API in Accept with media parameters' do
it 'fails with 406 Not Acceptable' do
env = { 'HTTP_ACCEPT' => 'application/vnd.api+json; charset=utf-8' }

expect(described_class.new(app).call(env)[0]).to eq(406)
end
end

context 'when receiving JSON API in Accept with media parameters among others' do
it 'fails with 406 Not Acceptable' do
env = { 'HTTP_ACCEPT' => 'application/json, application/vnd.api+json; charset=utf-8' }

expect(described_class.new(app).call(env)[0]).to eq(406)
end
end
end
6 changes: 6 additions & 0 deletions spec/railtie_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@
it 'registers the params parser for the JSON API MIME type' do
expect(::ActionDispatch::Request.parameter_parsers[:jsonapi]).not_to be_nil
end

it 'registers the FilterMediaType middleware' do
expect(
Rails.application.middleware.include?(JSONAPI::Rails::FilterMediaType)
).to be true
end
end