Kapitel 7. Mit Anwendungsfehlern elegant umgehen

Inhaltsverzeichnis

7.1. Einleitung
7.2. Der ErrorController und die Error-View
7.3. Gut, das hat nicht funktioniert...
7.4. Nicht alle Fehler sind gleich
7.5. Fazit

7.1. Einleitung

Unsere einfache, kleine Anwendung wird erwachsen! Um den Abschnitt des Buches abzuschließen, der Sie mit dem Zend Framework und dessen Grundlagen beim Aufsetzen einer Anwendung bekannt machen soll, werfen wir nun noch einen Blick auf die Fehlerbehandlung.

Wie wir im letzten Kapitel festgelegt haben, wirft Zend_Controller_Front nur Ausnahmen, wenn wir uns im Entwicklungs- oder Testmodus befinden. Im Produktivbetrieb sollten den Benutzern Fehler und Ausnahmen nicht angezeigt werden. Wenn etwas schief geht, können wir den Benutzer allerdings nicht mit einer leeren Browserseite abspeisen - er wird verwirrt, vielleicht sogar ratlos zurück bleiben. Wir sollten den Benutzer stattdessen darüber informieren, dass ein Problem existiert, dass es uns extrem leid tut, dass wir den zuständigen Entwickler sogleich foltern und diese kleine Panne beheben werden, sobald die Folter den gewünschten Effekt erzielt hat.

Um das zu erreichen, nimmt Zend_Controller_Front an, dass wir einen neuen Controller erzeugen, um diese Situation zu handhaben - den ErrorController, um genau zu sein. Schlägt die Erzeugung des ErrorController fehl, wird sich unsere Anwendung darüber beschweren, dass er fehlt, besonders wenn der Fehler von einer URL kommt, die auf einen nicht existierenden Ort verweist, weswegen eine 404-Meldung zurückgegeben werden sollte.

Die Behandlung von Ausnahmen erfolgt durch ein Front-Controller-Plugin namens Zend_Controller_Plugin_ErrorHandler.

7.2. Der ErrorController und die Error-View

Wir werden die Fehlerberichterstattung für den Benutzer mit dieser minimalen Klasse möglichst einfach halten.

Wie wir bereits festgestellt haben, rendern alle Controller-Actions automatisch eine zugehörige View. Da wir eine Methode errorAction() in einem ErrorController haben, wird das View-Skript in der Datei /application/views/scripts/error/error.phtml gerendert. Aber nur, wenn sie existiert - also fügen Sie sie nun hinzu!

Ach, wenn es bloß so einfach wäre...

Wechseln Sie zurück zum Browser und versuchen Sie, http://helloworld.tld/route/does/not/exist aufzurufen. Da diese URL keiner Route (und damit keinem Controller) entspricht, sollte die Anzeige dieser Fehlernachricht ausgelöst werden.

7.3. Gut, das hat nicht funktioniert...

Anstatt unserer Fehlerseite wurde eine Ausnahme-Meldung samt Details an den Browser gesendet.

Der ErrorController wurde so konzipiert, dass er nur anspringt, wenn der Front-Controller so eingestellt ist, dass er nie eine Ausnahme wirft - ausgenommen bei Ausnahmen, die geworfen werden müssen, weil jemand darauf vergessen hat, den ErrorController hinzuzufügen! Tatsächlich entstammt dieser Situation auch die irritierende Ausnahme-Meldung, dass kein ErrorController gefunden wurde. Um den ErrorController zu testen, müssen wir eine temporäre Veränderung an /config/application.ini vornehmen, die unterbindet, dass Zend_Controller_Front Ausnahmen wirft. Dazu ändern wir den Wert der letzte Zeile "resources.frontController.throwExceptions" auf 0 (bzw. false). Dadurch ist sichergestellt, dass das ErrorHandler-Plugin eine Fehlerseite ohne Ausnahmen ausliefern kann.

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
bootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php"
bootstrap.class = "ZFExt_Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.view.encoding = "UTF-8"
resources.view.doctype = "XHTML1_STRICT"
resources.view.contentType = "text/html;charset=utf-8"
resources.modifiedFrontController.contentType = "text/html;charset=utf-8"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.throwExceptions = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.throwExceptions = 0
[Wichtig] Wichtig

Vergessen Sie nicht, die Einstellung wieder auf den Originalwert 1 zurückzusetzen, sobald Sie mit diesem Kapitel fertig sind!

Versuchen Sie es jetzt noch einmal mit dieser URL...

7.4. Nicht alle Fehler sind gleich

Sie glauben vielleicht, dass wir nun aufhören und still und heimlich aus Folterkammer #7 flüchten können, aber da irren Sie sich. Wenn Sie einen genaueren Blick unter die Haube werfen, werden Sie feststellen, dass unsere Fehler sehr vage sind und wir - was schlimmer ist - auf die HTTP-Requests mit einem "HTTP/1.1 200 OK"-Header antworten. Nennen Sie mich dumm, aber haben wir nicht eben erst unseren Fehler eingestanden, indem wir dem Benutzer eine Fehlermeldung präsentiert haben? Wenn in unserer Anwendung ein Fehler auftritt, dann sollte niemals so ein Status-Code und so eine Nachricht zurückgegeben werden.

Das scheint vielleicht nicht sehr wichtig zu sein, aber das ist es. Nicht jeder Teril unserer Anwendung wird unbedingt Joe Bloggs in seinen Browser geliefert. Requests können von anderen Anwendungen kommen, die sich beim Feststellen von Fehlern eher auf HTTP-Response-Codes verlassen als auf Ihre unverhohlene Grausamkeit Entwicklern gegenüber. Auf jeden Fall müssen Sie mit Ihrer internen Anwendungsüberprüfung fertig werden. Werden Sie Probleme aufspüren können, wenn laut Ihren Webserverlogs jede einzelne Anfrage erfolgreich war? Dann gibt es noch das große Problem namens RFC 2616 "Hypertext Transfer Protocol -- HTTP/1.1", das für den Bereich der 2xx Status-Codes erklärt: "This class of status code indicates that the client's request was successfully received, understood, and accepted". Das W3C hat mit anderen Worten einen ganz speziellen Raum mit dem Namen "Folterkammer aus der Hölle #1" für diejenigen, deren Anwendungen behaupten, allmächtig zu sein.

Ein fehlgeschlagenes Routing ist kein Erfolg, sondern es bedeutet, dass entweder das Routing nicht funktioniert oder - was wahrscheinlicher ist - der Benutzer eine ungültige URL angefordert hat. Da das Verwenden einer falschen URL ein Benutzerfehler ist, sollten wir unseren Teamleiter vermutlich noch nicht in die Folterkammer schicken. Stattdessen sollten wir dem Besucher (höflich) erklären, dass er sich geirrt hat und einen 404 "Not Found"-Status für den Fall einfügen, dass der Benutzer eine Maschine ist. Um das umzusetzen, müssen wir in unserem ErrorController ein paar Kontrollen durchführen.

Im obigen Codeausschnitt schnappen wir uns das ErrorHandler-Plugin und lesen den Wert des Fehlertyps aus. Wenn der Fehler einem Routing-Problem entspricht, setzen wir den Statuscode 404, um klarzustellen, dass es sich nicht um einen Fehler von uns handelt und der Pfad in unserer Anwendung schlicht nicht existiert. Ansonsten setzen wir für alle anderen Fehler den HTTP-Status-Code 500. Der Status-Code 500 wird im Header durch die Meldung "Internal Server Error" ergänzt. Aktualisieren wir also unser vorhandenes Fehler-Template.

Kundenbindung ist eindeutig nicht ein vorrangiges Ziel dieses Kapitels.

Wenn Sie mit dem Browser eine ungültige URL aufrufen, sollten Sie die entsprechende Meldung zusammen mit dem Status-Code 404 im Header erhalten. Wenn Sie etwas tun, um eine andere Ausnahme zu werfen, sollten Sie den Status-Code 500 mit der zugehörigen HTML-Meldung erhalten.

7.5. Fazit

Fehlerbehandlung ist keine schwierige, aber eine notwendige Aufgabe - besonders dann, wenn unsere Anwendung von anderen anwendungsgesteuerten Clients besucht werden könnte, die sich nicht auf von Menschen lesbare HTML-Seiten, sondern auf die Kommunikation mittels korrekter Status-Codes und -Header verlassen, um abzuklären, ob ein HTTP-Request erfolgreich war oder nicht. Ich empfehle Ihnen wärmstens, darauf zu achten, dass Ihre Anwendung immer korrekte Header und Codes sendet. Der ErrorController ist ziemlich einfach gehalten, aber man kann sich leicht denken, dass wir ihn um das Loggen von Fehlern oder E-Mail-Benachrichtigungen erweitern könnten.