2010-11-08 5 views
7

Je suis sur Rails 2.3.5. Dans un contrôleur typique utilisateur de créer une actionErreur de chaîne JSON non valide dans Rails

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

Lorsqu'un client passe une chaîne JSON invalide/malformé pour l'entrée, Rails lancer une 500 erreur de serveur interne disant « chaîne non valide JSON ». Est-il possible pour moi de piéger l'erreur afin que je puisse donner un message personnalisé?

MISE À JOUR Voici la pile de données demandée. Juste pour que je sois clair, je sais que c'est une chaîne JSON malformée, ma question n'est pas comment réparer la chaîne JSON mais plutôt comment piéger cette erreur particulière afin que je puisse renvoyer un message d'erreur plus significatif que HTTP 500 Erreur interne du serveur . Merci d'avance pour votre aide

Error occurred while parsing request parameters. 
Contents: 

user: {login: "John", email: "[email protected]", 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 
+0

pouvez-vous mettre un backtrace complet, s'il vous plaît? – shingara

Répondre

6

J'ai trouvé ce plugin de rails pour fonctionner sur des rails 3.0.5: https://github.com/kares/request_exception_handler

Comme Rob Cameron suggère il charge votre demande rails et soulève la parseError de votre code d'application. Je n'ai pas testé son impact sur les performances, mais cela semble bien fonctionner.

+0

Ce plugin a fonctionné comme un charme! – Myxtic

0

regardant dans /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.8/lib/active_support/json/backends/yaml.rb (certes un peu différent version), il semble qu'une ParseError soit en train d'être levée. En supposant que l'erreur se produit à « format.json », vous pouvez essayer quelque chose comme

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

Si cela ne fonctionne pas, vous pouvez essayer de l'attraper plus tôt en utilisant la fonction de rappel de rescue_from dans votre contrôleur.

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

Merci pour la suggestion, malheureusement, l'erreur n'est pas soulevée à format.json. Je crois qu'il est levé avant que l'action user/create ne soit appelée car Rails a besoin d'analyser la chaîne JSON pour avoir accès aux params. – Bob

+0

Ah, je suppose que cela a du sens. Dans ce cas, essayez le rappel rescue_from. J'ai ajouté un exemple ci-dessus. – bioneuralnet

+1

Une autre bonne idée mais hélas, pas de dés, l'erreur est soulevée avant même qu'il ne frappe le contrôleur – Bob

0

Je suis en cours d'exécution dans le même problème en ce moment - j'ai une API que quelqu'un pourrait passer JSON invalide à et je veux être intelligent sur l'erreur que je reviens. Je cours Rails 3.0.5. Je traçais l'erreur étant jeté à lib/active_support/json/backends/yaml.rb ligne 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 

Ainsi, json.read échoue et jette l'erreur. Malheureusement cela se produit bien avant que votre contrôleur ne soit invoqué (cela fait partie de l'analyse initiale des paramètres de requête où Rails est lié à Rack). Je pense que pour attraper ceci, vous devez ajouter un petit correctif de singe pour écraser certaines erreurs de lancement intégrées et faire une bulle à la place de quelque chose que vous pouvez utiliser ... peut-être pourriez-vous ajouter un en-tête à l'objet de requête que vous pourriez alors détecter dans votre contrôleur et lancer votre propre erreur.

Malheureusement je ne pense pas que cela fonctionnera pour moi puisque je veux seulement remplacer le comportement lors de l'accès à l'API, pas le reste du site. Bien que je suppose dans le patch de singe, en supposant que j'ai accès à l'objet request, je pourrais lui dire d'utiliser mon nouveau comportement si le chemin demandé correspond à une regex comme /\/api\//

+0

J'ai résolu ce problème en utilisant Rack middleware. Le middleware est inséré avant ActionDispatch :: ParamsParser et récupère les erreurs émises par @ app.call.Comme les actions du contrôleur sont également des applications en rack dans les applications rails, je peux utiliser une action du contrôleur pour afficher un joli message d'erreur en utilisant tous les modèles de rails. (Affichage pour d'autres personnes qui obtiennent ici de Google) https://gist.github.com/4709748 –