First:
I'm using grape for building my API (Rails 4). When someone is sending an invalid JSON body (e.g. forgot the last }
), the following error is raised:
ActionDispatch::ParamsParser::ParseError (795: unexpected token at '{"foobar": 1234
')
I tried it with grapes rescue_from :all
option, but this doesn't work. Inside the stacktrace, I don't see the grape gem involved. It seems that this error is thrown from actionpack:
.gems/gems/actionpack-4.1.4/lib/action_dispatch/middleware/params_parser.rb:53:in `rescue in parse_formatted_parameters'
.gems/gems/actionpack-4.1.4/lib/action_dispatch/middleware/params_parser.rb:32:in `parse_formatted_parameters'
.gems/gems/actionpack-4.1.4/lib/action_dispatch/middleware/params_parser.rb:23:in `call'
But what would be the best way to catch those errors, return a 400: Bad Request
errors, and include the unexpected token at '{"foobar": 1234
message inside the json response?
Second:
I tried to test this with RSpec, but didn't have any luck on sending a raw request with an invalid JSON. I tried it with
post "/my_route", '{some invalid json'
but this doesn't throw the error from above. I thought since Rails 4, the second parameter, passed as a string, is treated like the raw body?
Unfortunately ActionDispatch runs well ahead of it ever getting to a controller so you're not going to be able to do this with Grape (AFAIK).
We ran into this issue too and found a wonderful article from the Thoughtbot guys on the subject.
Use the Curb gem to make the shotty calls:
require 'curb'
it 'sends poorly formatted json' do
broken_json = %Q|{"notice":{"title":"A sweet title"#{invalid_tokens}}}|
resp = Curl.post("http://#{host}:#{port}#{path}", broken_json)
expect(resp.response_code).to eq 500
end
Thoughtbot recommends writing a middleware class to capture future JSON parse errors like this:
# in app/middleware/catch_json_parse_errors.rb
class CatchJsonParseErrors
def initialize(app)
@app = app
end
def call(env)
begin
@app.call(env)
rescue ActionDispatch::ParamsParser::ParseError => error
if env['HTTP_ACCEPT'] =~ /application\/json/
error_output = "There was a problem in the JSON you submitted: #{error}"
return [
400, { "Content-Type" => "application/json" },
[ { status: 400, error: error_output }.to_json ]
]
else
raise error
end
end
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With