Attrapez toutes les exceptions dans un contrôleur de rails

89

Existe-t-il un moyen d'attraper toutes les exceptions non corrigées dans un contrôleur de rails, comme ceci:

def delete
  schedule_id = params[:scheduleId]
  begin
    Schedules.delete(schedule_id)
  rescue ActiveRecord::RecordNotFound
    render :json => "record not found"
  rescue ActiveRecord::CatchAll
    #Only comes in here if nothing else catches the error
  end
  render :json => "ok"
end

Je vous remercie

Neigaard
la source

Réponses:

92
begin
  # do something dodgy
rescue ActiveRecord::RecordNotFound
  # handle not found error
rescue ActiveRecord::ActiveRecordError
  # handle other ActiveRecord errors
rescue # StandardError
  # handle most other errors
rescue Exception
  # handle everything else
  raise
end
Chris Johnsen
la source
38
La règle n'est-elle pas de ne JAMAIS attraper une exception?
RonLugge
2
mais comment puis-je attraper tous les types en rescue => ebloc uniquement?
Matrix
7
@RonLugge cela dépend entièrement de la situation actuelle. appliquer «jamais» en règle générale est une mauvaise idée.
Justin Skiles
11
@JustinSkiles Catching Exception détectera les erreurs de syntaxe (et interrompra également les signaux). Donnez-moi un bon scénario pour faire cela dans le code de production. Je peux obtenir des signaux directement, mais vous devez le faire explicitement pour indiquer clairement que vous créez un gestionnaire de signaux. Juste attraper Exception ... mauvaise, mauvaise idée. Attrape même les choses que vous ne devriez pas essayer d'attraper.
RonLugge
6
L'un des rares cas courants où il est raisonnable de récupérer d'Exception est à des fins de journalisation / de rapport, auquel cas vous devez immédiatement relancer l'exception: stackoverflow.com/a/10048406/252346
aelesbao
198

Vous pouvez également définir une méthode rescue_from.

class ApplicationController < ActionController::Base
  rescue_from ActionController::RoutingError, :with => :error_render_method

  def error_render_method
    respond_to do |type|
      type.xml { render :template => "errors/error_404", :status => 404 }
      type.all  { render :nothing => true, :status => 404 }
    end
    true
  end
end

En fonction de votre objectif, vous pouvez également envisager de NE PAS gérer les exceptions sur une base par contrôleur. À la place, utilisez quelque chose comme le gem exception_handler pour gérer les réponses aux exceptions de manière cohérente. En prime, cette approche gérera également les exceptions qui se produisent au niveau de la couche middleware, comme l'analyse des demandes ou les erreurs de connexion à la base de données que votre application ne voit pas. Le gem exception_notifier peut également être intéressant.

BOFH
la source
4
Ceci est d'autant plus pratique que cela permet de capturer les exceptions de manière DRY.
m33lky
Et si j'utilise rescue_from sans paramètres? cela se comportera-t-il de la même manière que le sauvetage? attraper toutes les erreurs?
minohimself
2
N'est-ce pas une mauvaise pratique rescue_from Exception? Je crois comprendre qu'il vaut mieux sauver de StandardError, donc des choses comme SyntaxErroret LoadErrorne sont pas prises.
lobati
Oui, c'est une mauvaise forme de sauver «Exception». Voir "Exceptional Ruby" d'Avdi Grimm pour les raisons pour lesquelles cela peut être problématique.
Midwire
34

Vous pouvez intercepter les exceptions par type:

rescue_from ::ActiveRecord::RecordNotFound, with: :record_not_found
rescue_from ::NameError, with: :error_occurred
rescue_from ::ActionController::RoutingError, with: :error_occurred
# Don't resuce from Exception as it will resuce from everything as mentioned here "http://stackoverflow.com/questions/10048173/why-is-it-bad-style-to-rescue-exception-e-in-ruby" Thanks for @Thibaut Barrère for mention that
# rescue_from ::Exception, with: :error_occurred 

protected

def record_not_found(exception)
  render json: {error: exception.message}.to_json, status: 404
  return
end

def error_occurred(exception)
  render json: {error: exception.message}.to_json, status: 500
  return
end
mohamed-ibrahim
la source
2
Attention à ne pas sauver Exceptiondirectement; voir stackoverflow.com/questions/10048173/…
Thibaut Barrère
10

rescue sans arguments sauvera toute erreur.

Alors, vous voudrez:

def delete
  schedule_id = params[:scheduleId]
  begin
    Schedules.delete(schedule_id)
  rescue ActiveRecord::RecordNotFound
    render :json => "record not found"
  rescue
    #Only comes in here if nothing else catches the error
  end
  render :json => "ok"
end
PreciousBodilyFluides
la source
8
Question périmée, mais cette réponse est incorrecte. sauvetage sans argument gère uniquement StandardError robots.thoughtbot.com/rescue-standarderror-not-exception
Keith Gaddis
0

En fait, si vous voulez vraiment tout attraper , il vous suffit de créer votre propre application d'exceptions, qui vous permet de personnaliser le comportement qui est généralement géré par le middleware PublicExceptions: https://github.com/rails/rails/blob/4-2 -stable / actionpack / lib / action_dispatch / middleware / public_exceptions.rb

Un tas d'autres réponses partagent des joyaux qui font cela pour vous, mais il n'y a vraiment aucune raison que vous ne puissiez pas simplement les regarder et le faire vous-même.

Une mise en garde: assurez-vous de ne jamais lever d'exception dans votre gestionnaire d'exceptions. Sinon, vous obtenez un moche FAILSAFE_RESPONSE https://github.com/rails/rails/blob/4-2-stable/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L4-L22

BTW, le comportement du contrôleur provient de rescuable: https://github.com/rails/rails/blob/4-2-stable/activesupport/lib/active_support/rescuable.rb#L32-L51

BF4
la source