Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catching Invalid JSON Parse Errors with Rack Middleware

I am using Rails 5 and I am trying to improve error handling for invalid JSON requests to my API.

I tried handling invalid format JSON by parsing in the controller with a rescue but realised that Rails middleware is parsing my JSON request before it hits the controller if a user adds Content Type to their request header.

I followed the below guide: https://robots.thoughtbot.com/catching-json-parse-errors-with-custom-middleware

However, I get the following error when starting the server:

.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/stack.rb:108:in `assert_index': No such middleware to insert before: ActionDispatch::ParamsParser (RuntimeError)

Now, what this means is that ActionDispatch::ParamsParser isn't running. I think that it is deprecated in Rails 5 so that rules out that option.

I also tried to use rescue_from in my API Controller:

rescue_from JSON::ParserError, with: :json_error

def json_error
  render status: 400, json: {error: "Invalid JSON format in request body"}, status: :unprocessable_entity
end

However, this also did not work. It seems to skip past it.

Or if I try this:

rescue_from JSON::ParserError, with: json_error

def json_error
  render status: 400, json: {error: "Invalid JSON format in request body"}, status: :unprocessable_entity
end

I get:

undefined local variable or method `json_error' for Api::ApiController:Class
actionpack (5.0.0.1) lib/action_dispatch/routing/route_set.rb, line 46

``` ruby
   41         private
   42   
   43           def controller(req)
   44             req.controller_class
   45           rescue NameError => e
>  46             raise ActionController::RoutingError, e.message, e.backtrace
   47           end
   48   
   49           def dispatch(controller, action, req, res)
   50             controller.dispatch(action, req, res)
   51           end

Getting very lost, could use some guidance

like image 288
pronoob Avatar asked Dec 16 '16 17:12

pronoob


1 Answers

Seems the guide above is outdated with Rails 5. After some investigation, it seems that the following middleware is no longer called:

config/application.rb

config.middleware.insert_before ActionDispatch::ParamsParser, "CatchJsonParseErrors"

I modified it to be:

require "./lib/middleware/catch_json_parse_errors.rb"
config.middleware.insert_before Rack::Head, CatchJsonParseErrors

This is because Rack::Head is in the middleware stack, but ActionDispatch::ParamsParser is not. Also, the use of Class names as strings is deprecated, so you need to require the file and then pass in the class.

I also modified the below class to check env['CONTENT_TYPE'] instead of env['HTTP_ACCEPT']

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

  def call(env)
    begin
      @app.call(env)
    rescue ActionDispatch::ParamsParser::ParseError => error
      if env['CONTENT_TYPE'] =~ /application\/json/
        error_output = "There was a problem in the JSON you submitted: #{error.class}"
        return [
          400, { "Content-Type" => "application/json" },
          [ { status: 400, error: error_output }.to_json ]
        ]
      else
        raise error
      end
    end
  end
end
like image 68
pronoob Avatar answered Dec 08 '22 06:12

pronoob