Capture todas las excepciones en un controlador de rieles

89

¿Hay alguna forma de capturar todas las excepciones no detectadas en un controlador de rieles, como esta?

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

Gracias

Neigaard
fuente

Respuestas:

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
fuente
38
¿No es la regla NUNCA atrapar una excepción?
RonLugge
2
pero ¿cómo puedo capturar todo tipo en rescue => ebloque solamente?
Matrix
7
@RonLugge, depende completamente de la situación en cuestión. aplicar "nunca" como regla general es una mala idea.
Justin Skiles
11
@JustinSkiles Catching Exception detectará errores de sintaxis (y también interrumpirá las señales). Dame un buen escenario para hacer eso en código de producción. Puedo captar señales directamente, pero deberías hacerlo explícitamente para dejar en claro que estás creando un controlador de señales. Solo atrapando Exception ... mala, mala idea. Atrapa incluso las cosas que no deberías intentar atrapar.
RonLugge
6
Uno de los pocos casos comunes en los que es sensato rescatar de Exception es para fines de registro / informes, en cuyo caso debe volver a generar inmediatamente la excepción: stackoverflow.com/a/10048406/252346
aelesbao
198

También puede definir un método 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

Dependiendo de cuál sea su objetivo, es posible que también desee considerar NO manejar excepciones por controlador. En su lugar, use algo como la gema exception_handler para administrar las respuestas a las excepciones de manera consistente. Como beneficio adicional, este enfoque también manejará las excepciones que ocurren en la capa de middleware, como el análisis de solicitudes o los errores de conexión de la base de datos que su aplicación no ve. La gema exception_notifier también puede ser de interés.

BOFH
fuente
4
Esto es aún más útil ya que permite detectar excepciones de manera SECA.
m33lky
¿Y si uso rescue_from sin parámetros? ¿Se comportará igual que el rescate? detectar todos los errores?
minohimself
2
¿No es una mala práctica rescue_from Exception? Tengo entendido que es mejor rescatar StandardError, por lo que las cosas como SyntaxErrory LoadErrorno se capturan.
lobati
Sí, es de mala forma rescatar 'Exception'. Vea "Exceptional Ruby" de Avdi Grimm para conocer las razones por las que eso puede ser problemático.
Midwire
34

Puede detectar excepciones por tipo:

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
fuente
2
Cuidado de no rescatar Exceptiondirectamente; ver stackoverflow.com/questions/10048173/…
Thibaut Barrère
10

rescue sin argumentos rescatará cualquier error.

Entonces, querrás:

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
PreciosoFluidos Corporales
fuente
8
Pregunta obsoleta, pero esta respuesta es incorrecta. rescate sin argumento maneja solo StandardError robots.thoughtbot.com/rescue-standarderror-not-exception
Keith Gaddis
0

En realidad, si realmente desea capturar todo , simplemente cree su propia aplicación de excepciones, que le permite personalizar el comportamiento que generalmente maneja el middleware PublicExceptions: https://github.com/rails/rails/blob/4-2 -stable / actionpack / lib / action_dispatch / middleware / public_exceptions.rb

Muchas de las otras respuestas comparten gemas que hacen esto por ti, pero realmente no hay razón para que no puedas simplemente mirarlas y hacerlo tú mismo.

Una advertencia: asegúrese de nunca generar una excepción en su controlador de excepciones. De lo contrario, obtienes un feo FAILSAFE_RESPONSE https://github.com/rails/rails/blob/4-2-stable/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L4-L22

Por cierto, el comportamiento en el controlador proviene de rescuable: https://github.com/rails/rails/blob/4-2-stable/activesupport/lib/active_support/rescuable.rb#L32-L51

BF4
fuente