Reputation: 64941
I have a Rails API which accepts only JSON as input. If I fail to include a header of Content-Type: application/json, then request.headers['Content-Type']
defaults to application/x-www-form-urlencoded
and the params do not get parsed properly. The whole json body becomes a key in the params. The result is a 422, which is confusing to API users.
How can I change this to default to parsing as json if no Content-Type header is supplied?
Lots of other questions answer how to do this with the response format. To change this default, you can specify it in the controller with:
request.format = :json
Or in a route namespace with something like:
namespace :api, defaults: {format: :json} do
This, however, changes the default response format and does not change the default request format. What I need to do is to change the default request format for parsing parameters.
Upvotes: 4
Views: 3781
Reputation: 14227
I've solved it with middleware this way for Rails API (rails new my_project --api
)
config:
# config/application.rb
# ...
require './lib/middleware/consider_all_request_json_middleware'
# ...
module MyApplication
# ...
class Application < Rails::Application
# ...
config.middleware.insert_before(ActionDispatch::Static,ConsiderAllRequestJsonMiddleware)
# ...
middleware:
# lib/middleware/consider_all_request_json_middleware.rb
class ConsiderAllRequestJsonMiddleware
def initialize app
@app = app
end
def call(env)
if env["CONTENT_TYPE"] == 'application/x-www-form-urlencoded'
env["CONTENT_TYPE"] = 'application/json'
end
@app.call(env)
end
end
original: https://blog.eq8.eu/til/content-type-applicationjson-by-default-in-rails-5.html
Upvotes: 3
Reputation: 64941
Here is my admittedly terrible solution derived from the suggestion in Micael Nussbaumer's answer. I'd love it if some Rubyists could magically turn this ugly hack into a pithy one liner.
module Api
class BaseApiController < ActionController::API
private
# This is an ugly hack needed to make it default to json if you do not
# specify a Content-Type. If you see this and know of a better way please
# say so!
def params
if !@params
if request.headers["Content-Type"]=="application/x-www-form-urlencoded"
body_string = request.body.read
begin
hash = JSON.parse(body_string)
@params = ActionController::Parameters.new(hash)
rescue
# do nothing
end
end
if !@params
@params = super
end
end
@params
end
...
end
Upvotes: 3
Reputation: 2290
parsed = JSON.parse(json_body) unless request.headers["Content-Type"] == 'application/json'
Upvotes: 2