web-dev-qa-db-fra.com

Comment gérer des exceptions dans le code reposant basé sur JSON?

J'ai une application "logicielle en tant que service" qui utilise Json communiquée via une API reposante.

Simplement indiqué: Quelles sont les meilleures pratiques pour la capture et la signalisation des exceptions lors de l'utilisation d'une API reposante avec l'échange de données JSON?

Ma première pensée était de voir ce que Rails le fait en générant un échafaud, mais ce n'est clairement pas raison. Voici un extrait:

class MumblesController < ApplicationController

  # GET /mumbles/1
  # GET /mumbles/1.json
  def show
    @mumble = Mumble.find(params[:id])
    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @mumble }
    end
  end

end

Dans ce cas, si le code JSON envoie un identifiant inexistant, par exemple.

http://www.myhost.com/mumbles/99999.json

puis mumble.find () va élevera Activerecord :: recordNotfound. ActionController attrapera cela et rendre une page d'erreur dans HTML. Mais HTML est inutile au client qui attend JSON.

Je pourrais contourner cela en enveloppant le Mumble.Find () dans un begin ... rescue RuntimeError bloquer et rendu un statut JSON =>: non productible ou quelque chose.

Mais alors si l'application du client envoie un chemin non valide, E.G.:

http://www.myhost.com/badtypo/1.json

Une application basée sur JSON est-elle censée capter cela et retourner une erreur dans Json? Si oui, où puis-je capturer cela sans creuser profondément dans ActionDispatch?

Alors, Globalement, est-ce que je punt et que l'actionController générer HTML s'il y a une erreur? Ça ne se sent pas juste ...

37
fearless_fool

(J'ai trouvé la réponse juste avant que je frappe [post votre question]. Mais cela pourrait aider quelqu'un d'autre aussi bien ...)

Utilisez ActionController's rescue_from

La réponse est d'utiliser l'actionController rescue_from, comme décrit dans ce guide et documenté ici . En particulier, vous pouvez remplacer le rendu par défaut des fichiers par défaut 404.html et 500.html dans ces lignes:

class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found

private
  def record_not_found(error)
    render :json => {:error => error.message}, :status => :not_found
  end 
end
77
fearless_fool

Si cela aide quiconque, c'est ce que j'ai fait comme une capture pour mon API purement JSON:

Dans votre ApplicationController que chaque contrôleur spécifique hérite de, ajouter

# app/controllers/api/v1/application_controller.rb

# ...

rescue_from StandardError do |exception|
    render json: { :error => exception.message }, :status => 500
end

# ...
  • basé surtout de la réponse de Fearless_fool
7
Tim S

En tant que développeur, vous voudrez également voir des traces (de préférence avec des lignes utiles, filtrer des gemmes). Et faire des traces invisibles pour la production:

  rescue_from StandardError do |exception|
    # Handle only JSON requests
    raise unless request.format.json?

    err = {error: exception.message}

    err[:backtrace] = exception.backtrace.select do |line|
      # filter out non-significant lines:
      %w(/gems/ /rubygems/ /lib/Ruby/).all? do |litter|
         not line.include?(litter)
      end
    end if Rails.env.development? and exception.is_a? Exception

    # duplicate exception output to console:
    STDERR.puts ['ERROR:', err[:error], '']
                    .concat(err[:backtrace] || []).join "\n"

    render :json => err, :status => 500
  end
3
Daniel Garmoshka

Il n'y a pas de consensus clair sur la manière de conserver une norme cohérente pour la rédaction du code API JSON, mais c'est une partie de ce que je pratique (plus que ce que vous avez demandé):

  1. Gardez-le simple - essayez de rester reposant. Les méthodes personnalisées peuvent rendre les choses rapidement complexes.
  2. Avoir le serveur renvoyer des codes d'erreur native et utilisez 'sauve_from' pour capturer et
  3. dans d'autres cas, rendu Rails code de réponse HTTP, pouvant être spécifiquement ciblé par l'application client.

Dans votre cas, vous pourriez trouver le Rails répond_to et réponse_with gérer HTML/JSON/Autres réponses avec Grace. Et même dans votre solution, cela rendra effectivement rendu le HTML, mais ce n'est pas ce que Sera interprété par votre application client, qui va plutôt lire l'en-tête HTTP et obtenir le code de réponse HTTP, ce qui est ce qui déclenche votre "sauvetage_from".

1
Carson Cole