16

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":"foobar@mail.net","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. :(

Mich
  • 729
  • 1
  • 7
  • 14
  • i think you need use [wrap_parameters()](http://apidock.com/rails/ActionController/ParamsWrapper/ClassMethods/wrap_parameters) in you `ActionController` – Roman Kiselenko Apr 10 '14 at 13:40
  • `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. – Mich Apr 10 '14 at 13:46

6 Answers6

2

Check this SO answer. It uses constraints as follows

defaults format: :json do
  # your v1/registration route here
end
Tun
  • 1,345
  • 13
  • 34
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
ErvalhouS
  • 4,178
  • 1
  • 22
  • 38
0
class V1::RegistrationsController < ApplicationController
  respond_to :json
end

Makes the default reponse format json

  • 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. – ivan Aug 14 '19 at 15:46
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.

rjmAmaro
  • 618
  • 8
  • 20
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' }
Nico Brenner
  • 690
  • 7
  • 11
-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.

Community
  • 1
  • 1
house9
  • 20,359
  • 8
  • 55
  • 61
  • 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(#) _ request.format.to_s => "application/json" [3] api(#) _ request.content_type => "text/plain" [4] api(#) _ params => { "format" => :json, "action" => "create", "controller" => "v1/registrations" } :( – Mich Apr 10 '14 at 16:03
  • 1
    request.format does not change request.content_type – Mich Apr 10 '14 at 16:29
  • I see your issue, its with the request (content_type) not the response (format) – house9 Apr 10 '14 at 16:31
  • 1
    Yes only with the request ;) – Mich Apr 10 '14 at 16:40