Während der Entwicklung tritt bei dem ein oder anderen Entwickler doch mal ein Fehler auf, auf den uns Rails mit einem Exception-Text und dem Stacktrace hinweist. So lassen sich die Fehler meist schnell finden und beseitigen. Im produktiven Einsatz sollte sowas zwar nicht mehr passieren, aber falls doch, wird mittels des Status-Codes 500 auf den internen Fehler hingewiesen und das Log mit der Exception gefüllt.

Doch was ist, wenn so ein Fehler in Mitten eines AJAX-Aufrufs passiert? Dann bleibt der Request auf dem Server hängen und der Browser erhält nie eine Antwort. Der Nutzer (egal ob Entwickler oder Endanwender) erhält keine Rückmeldung.

Das lässt sich ändern, indem man die rescue_from-Methode von Rails benutzt. Sie ist dafür gedacht, auf spezielle Exceptions zu reagieren - besonders auf welche, die vom Entwickler definiert wurden.
Wir wandeln diesen Gedanken ab und fangen im ApplicationController alle Fehler ab:


# Alle Fehler durch eigene Methode abfangen
rescue_from Exception, :with => show_javascript_errors

Bevor jetzt die ersten Schmerzensschreie zu hören sind, soll schnell gesagt werden, dass im ersten Schritt der behandelnden Methode sichergestellt wird, dass Nicht-Javascript-Requests von ihr nicht betroffen sind. Hier ist die Fehlerbehandlung von Rails einfach ausreichend.

Was können wir nun tun, wenn während eines AJAX-Requests ein Fehler auftrat? Wir können einen HTTP-Request draus machen, indem wir auf eine normale Action weiterleiten. Wir haben uns für die "index"-Action des aktuellen Controllers entschieden, weil AJAX-Links ja typischerweise beim Einstellen bestimmter Filter einer Liste oder ähnlichem vorkommen. Sind kritischere Aktionen im Gang, ist es vielleicht sinnvoll, gleich zu einer allgemeinen Fehlerseite weiterzuleiten.

  def show_javascript_errors(exception)    
    respond_to do |format|

      format.html {rescue_action_without_handler(exception)}
      format.xml  {rescue_action_without_handler(exception)}
      format.js do
        log_error(exception) if logger

        if (ENV['RAILS_ENV'] == 'development')
          flash[:error] = exception.clean_message
        else
          flash[:error] = "Ein interner Fehler ist aufgetreten!"
        end
        render :update do |page|
          page.redirect_to {:action => 'index'}
        end
      end
    end
  end

In der Entwicklung bekommt man mit dem Fehlertext eine erste Idee, woran es denn hapert. Andernfalls wird - ähnlich zur 500er-Seite - ein allgemeiner Fehler ausgegeben und der Nutzer weiß wenigstens, woran er ist. Der genaue Fehler wird natürlich auch ins Log geschrieben.

Und wenn es nun ein grober Fehler ist, wie eine Datenbankverbindung, die soeben gekappt wurde? Dann wird natürlich der Aufruf der "index"-Action genauso scheitern und eine Exception bzw. die 500er-Seite anzeigen - so, als wäre es von Anfang an ein HTTP-Request gewesen.