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\//
精彩评论