19

I have a controller method in Ruby on Rails 3 that accepts application/JSON as the content type. This all works as expected, but I actually do not want rails to automatically parse the JSON in the body of the POST request. This method is acting as a gateway and just shuttles the information in to a queue and could be quite large. I do not want to waste the time processing the data in to the @_params since it's unnecessary.

I believe I could get around this by setting the content-type in the header of the request to something else, but I would like to be semantically correct for the HTTP requests.

How can I disable this functionality?

EDIT: more specifically how can I edit this functionality for just this one route?

Macdiesel
  • 935
  • 1
  • 9
  • 19
  • You know, if you Post it as JSON and tell rails to expect it to be posted as JSON, I think that Rails will always parse. To do what you're describing I think you almost want to mount a rack endpoint on that route and handle the POST with a utility like `rack_raw_upload`... Good question, sorry I don't really know the answer. – Andrew May 02 '12 at 04:30
  • Interesting thought... I may give that a shot and see how it goes. – Macdiesel May 02 '12 at 18:01

3 Answers3

13

Parameter parsing is baked pretty deeply inside actionpack's lib/action_dispatch/middleware/params_parser.rb.

I'd say the best you're going to get away with is intercepting the request with Rack, something like this.

In lib/raw_json.rb

module Rack
  class RawJSON
    def initialize(app)
      @app = app
    end

    def call(env)
      request = Request.new(env)
      if request.content_type =~ /application\/json/i
        # test request.path here to limit your processing to particular actions
        raw_json = env['rack.input'].read
        env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
        env['rack.input'] = StringIO.new("raw_json=#{raw_json}")
      end
      return @app.call(env)
    end
  end
end

In config.ru, insert this before the call to run <your app name>::Application

require 'raw_json'
use Rack::RawJSON
Mark Paine
  • 1,874
  • 13
  • 14
1

The overhead for parsing the JSON from a single request is relatively low. Do you have a specific reason to believe that processing the JSON is contributing to any kind of slowness in overall processing?

If not, then I'd recommend that you leave this as it is until such time is it can be identified as a problem.

Configuring rails to not parse that JSON (either via some sort of rack configuration or in some other method) will create at lest one route in your application that is not handled in a standard Rails Way.

Eventually, you may find yourself having to do some sort of processing of this data. Or maybe you'll need to put some sort of security in front of it. Or maybe you'll want to log it and related it to the user that sent it. And when that day comes you (or someone else on your team) will need to go in and make changes to this non-standard implementation.

Doing things in a non-standard way in a Rails implementation can add to the complexity and time to maintain the software. It is also a common source of defects since people are less familiar with non-standard processing.

So unless it is a real issue, I'd recommend just letting rails process the JSON and then simply pass it through the normal Rails Way.

Kevin Bedell
  • 13,254
  • 10
  • 78
  • 114
  • 1
    This doesn't answer the question. I am looking to not parse the incoming json response mostly for the sake of optimization. The json body could potentially be an array with tens of thousands of items and having to parse and iterate over this will take both time and memory away from the application when it could/should be doing other things. This action is merely a pass through for the data. – Macdiesel May 08 '12 at 15:24
  • It is NOT relatively low at all, Rails parses whole input, filters and logs ALL parameters and the cost is HUGE when dealing with many 100k sized requests. – lzap Aug 02 '16 at 12:02
1

It may be baked in, but looking at the code:

 module ActionDispatch
  class ParamsParser
    DEFAULT_PARSERS = {
      Mime::XML => :xml_simple,
      Mime::JSON => :json
    }

    def initialize(app, parsers = {})
      @app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
    end

     [ ... ]

        strategy = @parsers[mime_type]

So, if you can arrange to send a hash to this initializer, you can add or override the default. Not sure the allow for removing, but an empty parser method could work.

Parsers answer here: How do I initialize ActionDispatch::ParamsParser in Rails 3.1?

Code is from actionpack-3.2.8/lib/action_dispatch/middleware/params_parser.rb

Community
  • 1
  • 1
Gerry Gleason
  • 381
  • 2
  • 12