Rich Apodaca
Rich Apodaca

Reputation: 29004

Parsing JSON Payload in Rails When Using Custom MIME Type

A Rails application with a RESTful interface needs to accept POST data using a custom MIME type based on 'application/json'. Let's call that MIME type 'application/vnd.com.example.Foo+json'.

Registering the custom MIME type in Rails using Mime::Type.register works fine in that the application recognizes the MIME type given in the "Accept:" header and renders a JSON view through respond_to.

The problem is with controller methods such as update and create that need to convert custom JSON formatted data into a params hash. If I set the "Content Type:" header to application/json, the data gets parsed into the params hash.

But if I set the "Content Type:" header to 'applcation/vnd.com.example.Foo+json', then the payload does not get parsed.

So it seems like MIME::Type.register is used for driving the respond_to block, but not in deciding how to parse payloads for create and update methods.

Any ideas?

Upvotes: 8

Views: 4245

Answers (4)

unicornzero
unicornzero

Reputation: 71

Here's what I learned working with this in Rails 5:

Rich's information was a great starting point and, like him, I added my code in an initializer (in config/initializers/mime_types.rb).

In my scenario, when working with a custom content type that can be parsed as regular json, it's simple to add another synonym to the json mime type like this:

json_mime_type_synonyms = %w[
  text/x-json
  application/jsonrequest
  application/foobar+json
]
Mime::Type.register('application/json', :json, json_mime_type_synonyms)

If you take that approach, be sure to include the other synonyms (they're from the default json mime type) to ensure they continue to work properly.

Alternatively, if you need custom parsing logic or prefer separate mime types, here is how to both register the mime type and its associated parameter parser:

Mime::Type.register('application/foobar+json', :foobar_json)
ActionDispatch::Request.parameter_parsers[:foobar_json] = -> (body) {
  JSON.parse(body)
}

Upvotes: 2

Ryan Bigg
Ryan Bigg

Reputation: 107718

In Rails 5 do:

ActionDispatch::Request.parameter_parsers[Mime::Type.lookup('application/vnd.api+json').symbol] = lambda do |body|
  JSON.parse(body)
end

Upvotes: 5

Ginkgochris
Ginkgochris

Reputation: 465

In Rails 3 do:

ActionDispatch::ParamsParser::DEFAULT_PARSERS[Mime::Type.lookup('application/vnd.com.example.foo+json')]=lambda do |body|
  JSON.parse(body)
end

Upvotes: 4

Rich Apodaca
Rich Apodaca

Reputation: 29004

For those who may be interested, I found the answer to my own question.

Use something like this in mime_types.rb (or possibly elsewhere in your your initialization sequence):

ActionController::Base.param_parsers[Mime::Type.lookup('application/vnd.com.example.foo+json')]=lambda do |body|
  JSON.parse body
end

One catch: don't use capitalization in the MIME type above (i.e., 'application/vnd.com.example.Foo+json'). Rails converts the MIME type into all lower case, so no match will be found it it's set to upper case.

Upvotes: 6

Related Questions