开发者

Invalid JSON string error in Rails

I'm on Rails 2.3.5. In a typical user controller create action

class UsersController
  def create
    @user = User.new(params[:user])
    respond_to do |format]
      if @user.save ...
      else
        format.json ....
      end        
  end
end

When a client passes an invalid / malformed JSON string for input, Rails throw a 500 internal server error saying "Invalid JSON string". Is it possible for me to trap the error so I can give a custom message?

UPDATED Here's the stacktrace as requested. Just so I'm clear, I know this is a malformed JSON string, my question is not how to fix the JSON string but rather how to trap this particular error so I can send back a more meaningful error message than HTTP 500 Internal Server Error. Thanks in advance for your help

Error occurred while parsing request parameters.
Contents:

user: {login: "John", email: "john@yahoo.com", password: "111"}}
/!\ FAILSAFE /!\  Mon Nov 08 02:01:04 -0800 2010

  Status: 500 Internal Server Error
  Invalid JSON string
    c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.3.5/lib/active_support/json/backends/yaml.rb:14:in `decode'
 开发者_开发百科   c:1:in `__send__'
    c:1:in `decode'
    c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/params_parser.rb:42:in `parse_formatted_parameters'
    c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/params_parser.rb:11:in `call'
    c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/session/cookie_store.rb:93:in `call'
    c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/failsafe.rb:26:in `call'
    c:/ruby/lib/ruby/gems/1.8/gems/rack-1.0.1/lib/rack/lock.rb:11:in `call'
    c:/ruby/lib/ruby/gems/1.8/gems/rack-1.0.1/lib/rack/lock.rb:11:in `synchronize'
    c:/ruby/lib/ruby/gems/1.8/gems/rack-1.0.1/lib/rack/lock.rb:11:in `call'
    c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/dispatcher.rb:114:in `call'
    c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/reloader.rb:34:in `run'
    c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/dispatcher.rb:108:in `call'
    c:/ruby/lib/ruby/gems/1.8/gems/rails-2.3.5/lib/rails/rack/static.rb:31:in `call'
    c:/ruby/lib/ruby/gems/1.8/gems/rack-1.0.1/lib/rack/urlmap.rb:46:in `call'
    c:/ruby/lib/ruby/gems/1.8/gems/rack-1.0.1/lib/rack/urlmap.rb:40:in `each'
    c:/ruby/lib/ruby/gems/1.8/gems/rack-1.0.1/lib/rack/urlmap.rb:40:in `call'
    c:/ruby/lib/ruby/gems/1.8/gems/rails-2.3.5/lib/rails/rack/log_tailer.rb:17:in `call'
    ...
    c:/ruby/lib/ruby/gems/1.8/gems/rack-1.0.1/lib/rack/handler/mongrel.rb:34:in `run'
    c:/ruby/lib/ruby/gems/1.8/gems/rails-2.3.5/lib/commands/server.rb:111
    c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
    c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
    script/server:3
re.rb:31:in `require'
    script/server:3


I've found this rails plugin to work on rails 3.0.5: https://github.com/kares/request_exception_handler

As Rob Cameron suggests it loads your rails application and raises the parseError from your application code. I haven't tested it's performance impact, but it does seem to work well.


Looking in /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.8/lib/active_support/json/backends/yaml.rb (admittedly a slightly different version), it looks like a ParseError is being raised. Assuming the error is occurring at "format.json", you could try something like

begin
  format.json
rescue ParseError => e
  render :text => "Now there's some ugly JSON! (#{e.message})", :status => 500
end

If that doesn't work, you can try to catch it earlier using the rescue_from callback in your controller.

class UsersController < ApplicationController
...
  rescue_from ParseError do |e|
    render ...
  end
...
end


I'm running into the same issue right now -- I have an API that someone could pass invalid JSON to and I want to be smart about the error I return. I'm running Rails 3.0.5. I traced the error being thrown to lib/active_support/json/backends/yaml.rb line 17:

def decode(json)
  if json.respond_to?(:read)
    json = json.read
  end
  YAML.load(convert_json_to_yaml(json))
rescue ArgumentError
  raise ParseError, "Invalid JSON string"
end

So, json.read fails and throws the error. Unfortunately this happens long before your controller is ever invoked (it's part of the initial request params parsing where Rails ties into Rack). I would think that in order to catch this you would need to add a little monkey-patching to overwrite some of the built-in error throwing and instead bubble up something you can use...maybe you could add a header to the request object that you could then detect in your controller and throw your own error.

Unfortunately I don't think this will work for me since I only want to override the behavior when accessing the API, not the rest of the site. Although I suppose in the monkey-patch, assuming I have access to the request object, I could tell it to only use my new behavior if the requested path matches a regex like /\/api\//

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜