Pegue todas as exceções em um controlador de trilhos

89

Existe uma maneira de capturar todas as exceções não detectadas em um controlador Rails, como este:

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

Obrigado

Neigaard
fonte

Respostas:

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
fonte
38
A regra não é NUNCA pegar a exceção?
RonLugge
2
mas como posso pegar todos os tipos rescue => eapenas no bloco?
Matrix
7
@RonLugge isso depende inteiramente da situação em questão. aplicar "nunca" como regra prática é uma má ideia.
Justin Skiles
11
@JustinSkiles Catching Exception captura erros de sintaxe (e sinais de interrupção também). Dê-me um bom cenário para fazer isso no código de produção. Consigo captar sinais diretamente, mas você precisa fazer isso explicitamente para deixar claro que está criando um manipulador de sinais. Apenas pegando Exception ... má, má ideia. Pega até as coisas que você não deveria tentar pegar.
RonLugge
6
Um dos poucos casos comuns em que é sensato resgatar da Exceção é para fins de registro / relatório, caso em que você deve imediatamente relançar a exceção: stackoverflow.com/a/10048406/252346
aelesbao
198

Você também pode definir um 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

Dependendo de qual é o seu objetivo, você também pode considerar NÃO manipular exceções por controlador. Em vez disso, use algo como a gem exception_handler para gerenciar as respostas às exceções de forma consistente. Como um bônus, esta abordagem também tratará exceções que ocorrem na camada de middleware, como análise de solicitação ou erros de conexão de banco de dados que seu aplicativo não vê. A gema exception_notifier também pode ser de interesse.

BOFH
fonte
4
Isso é ainda mais prático, pois permite capturar exceções de maneira SECA.
m33lky
E se eu usar o rescue_from sem parâmetros? isso vai se comportar da mesma forma que resgate? pegar todos os erros?
minohimself
2
Não é uma má prática rescue_from Exception? Meu entendimento é que é melhor resgatar StandardError, então coisas como SyntaxErrore LoadErrornão são capturadas.
lobati
Sim, é incorreto resgatar 'Exception'. Veja "Ruby Excepcional" de Avdi Grimm para as razões pelas quais isso pode ser problemático.
Midwire
34

Você pode capturar exceções 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
fonte
2
Cuidado para não resgatar Exceptiondiretamente; consulte stackoverflow.com/questions/10048173/…
Thibaut Barrère
10

rescue sem argumentos resgatará qualquer erro.

Então, você vai querer:

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
PreciousBodilyFluids
fonte
8
Pergunta desatualizada, mas essa resposta está incorreta. resgate sem argumento lida apenas com StandardError robots.thoughtbot.com/rescue-standarderror-not-exception
Keith Gaddis
0

Na verdade, se você realmente deseja capturar tudo , basta criar seu próprio aplicativo de exceções, que permite personalizar o comportamento que geralmente é tratado pelo middleware PublicExceptions: https://github.com/rails/rails/blob/4-2 -stable / actionpack / lib / action_dispatch / middleware / public_exceptions.rb

Um monte de outras respostas compartilham joias que fazem isso por você, mas realmente não há razão para que você não possa simplesmente olhar para elas e fazer você mesmo.

Uma advertência: certifique-se de nunca lançar uma exceção em seu manipulador de exceções. Caso contrário, você obterá um FAILSAFE_RESPONSE feio https://github.com/rails/rails/blob/4-2-stable/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L4-L22

BTW, o comportamento no controlador vem de recuperável: https://github.com/rails/rails/blob/4-2-stable/activesupport/lib/active_support/rescuable.rb#L32-L51

BF4
fonte