Force all received requests Content-Type to JSON
Asked Answered
P

6

17

I'm actually working on an API which uses Rails 4. I would like to set the Content-Type of a request to JSON if the client does not specify a media type in Content-Type header.

In order to get that behaviour I tried to add the following before_action in my ApplicationController :

def set_request_default_content_type
  request.format = :json
end

In my RegistrationsController#create method I have a breakpoint to check if everything is working. Well, the request.format trick does not work, despite the value is set to application/json it seems that the controller (or Rails internals) do not consider the received request's Content-Type as JSON.

I did a POST request with the following body (and no Content-Type) :

{"user" : {"email":"[email protected]","password":"foobarfoo"}}

By debugging with Pry I see that :

 [2] api(#<V1::RegistrationsController>) _  request.format.to_s
 => "application/json"
 [3] api(#<V1::RegistrationsController>) _  params
 => {
       "action" => "create",
   "controller" => "v1/registrations"
 }

It means that Rails did not have considered my request with the request.format configured with Mime::JSON, but instead with Mime::ALL and so it didn't parse the request's JSON body. :(

Prostrate answered 10/4, 2014 at 13:25 Comment(2)
i think you need use wrap_parameters() in you ActionControllerBergmans
ActionController::ParamsWrapper is enabled by default for the json format. But it doesn't work. At the moment I don't use MetalController but a classic Rails architecture with standard controllers.Prostrate
A
2

Check this SO answer. It uses constraints as follows

defaults format: :json do
  # your v1/registration route here
end
Antonyantonym answered 11/12, 2019 at 18:44 Comment(0)
A
1

You could define a any type response inside the respond_to block, which will not restrict your controller to respond when request uri ends with .json, it also relief you from defining a response type explicitly and will keep, independently of request content-type, responding as you wish, example:

respond_to do |format|
  format.any  {render :json => {foo: 'bar'}}
end
Armstrong answered 15/2, 2018 at 15:34 Comment(0)
P
0
class V1::RegistrationsController < ApplicationController
  respond_to :json
end

Makes the default reponse format json

Proof answered 11/5, 2015 at 2:9 Comment(1)
The question was about the request content-type, not the response content-type. The request content-type affects how the server interprets the request, e.g. parsing params.Phyllous
C
0

Based on this post, it is possible to create some middleware that translates the header for Content-Type: application/json.

# config/application.rb
# ...
require './lib/middleware/consider_all_request_json_middleware'
# ...

module MyApplication
  # ...
  class Application < Rails::Application
    # ...
    config.middleware.insert_before(ActionDispatch::Static, ConsiderAllRequestJsonMiddleware)
    # ...
# lib/middleware/consider_all_request_json_middleware.rb

class ConsiderAllRequestJsonMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    env["CONTENT_TYPE"] = "application/json" if env["CONTENT_TYPE"] == "application/x-www-form-urlencoded"

    @app.call(env)
  end
end

I've tested it with a Rails 6 API-only project and it works properly.

Corissa answered 30/9, 2021 at 17:31 Comment(0)
P
0

You can use constraints in your routes.rb file to force the content_type.

Example on Rails 3:

match '/api/endpoint' => 'apis_controller#endpoint', constraints: lambda { |request| request.format = :json }

That line will make the Content-Type be json for all requests made to that route.

This other solution also worked for me when testing with rspec 2.99 and rspec-rails 2.99 on rails 3.0.6:

params = { username: 'username' }
post '/your_path', params.merge({format: 'json'}).to_json, { 'CONTENT_TYPE' => 'application/json', 'HTTP_ACCEPT' => 'application/json' }
Prichard answered 19/12, 2021 at 14:19 Comment(0)
L
-1

you should be able to set the default format using routes: http://guides.rubyonrails.org/routing.html#defining-defaults

resources :registrations, ... defaults: { format: 'json' }

see also: How to set the default format for a route in Rails? format-for-a-route-in-rails?answertab=active#tab-top


Also maybe of interest:

Rails ignores the accept header when it contains “,/” or “/,” and returns HTML (or JS if it’s a xhr request).

This is by design to always return HTML when being accessed from a browser.

This doesn’t follow the mime type negotiation specification but it was the only way to circumvent old browsers with bugged accept header. They had he accept header with the first mime type as image/png or text/xml.

Lovel answered 10/4, 2014 at 15:39 Comment(4)
I already tried to use config/route.rb's default format parameter and it basically does the same thing than my before_action : [2] api(#<V1::RegistrationsController>) _ request.format.to_s => "application/json" [3] api(#<V1::RegistrationsController>) _ request.content_type => "text/plain" [4] api(#<V1::RegistrationsController>) _ params => { "format" => :json, "action" => "create", "controller" => "v1/registrations" } :(Prostrate
request.format does not change request.content_typeProstrate
I see your issue, its with the request (content_type) not the response (format)Lovel
Yes only with the request ;)Prostrate

© 2022 - 2024 — McMap. All rights reserved.