Chapitre 7. Gérer les Erreurs Applicatives Gracieusement

Table des matières

7.1. Introduction
7.2. L'ErrorController et la Vue Error
7.3. Et bien... Cela n'a pas fonctionné...
7.4. Toutes les Erreurs ne sont pas Egales
7.5. Conclusion

7.1. Introduction

Notre petite et simple application est en train de grandir ! Pour terminer cette section du livre, conçue pour vous familiariser avec Zend Framework et ses étapes de base lorsqu'il s'agit de mettre en place une application, jetons un coup d'oeil à la gestion d'erreurs.

Tel que nous l'avons configuré au chapitre précédent, Zend_Controller_Front ne lévera d'exception que pour les environnements de développement ou de tests. En production, aucune erreur ni exception ne devrait être affichée aux utilisateurs. Cela dit, lorsque quelque chose ne se passe pas comme attendu, nous ne pouvons pas juste laisser l'utilisateur devant une fenêtre de navigateur vide, où il se demanderait tout d'abord avec quelque sorte d'amusement ce qu'il se passe... Jusqu'à ce que cet amusement se transforme peu à peu en agacement ! A la place, nous devrions informer l'utilisateur sur le fait qu'il y a un problème, que nous en sommes réellement désolé, que nous torturerons le développeur responsable aussi tôt que possible, et, espérons-le, que nous résoudrons ce problème dès que la séance de torture aura eu les effets désirés.

Pour permettre cela, Zend_Controller_Front suppose que nous créerons un nouveau Contrôleur, nommé ErrorController pour être précis, pour gérer ce type de situation. Si vous ne créez pas ErrorController, votre application se plaindra de son absence, en particulier lorsqu'une erreur sera causée par un accès via une URL qui pointe sur un emplacement qui n'existe pas ; ce qui devrait renvoyer une 404.

Cette gestion des Exceptions est effectuée par un Plugin Front Controller nommé Zend_Controller_Plugin_ErrorHandler.

7.2. L'ErrorController et la Vue Error

Nous ferons en sorte que nos rapports d'erreurs effectués auprès de l'utilisateur soient aussi simples que possibles, avec cette classe minimaliste :

Comme nous l'avons vu précédemment, toutes les Actions de Contrôleurs entraineront automatiquement le rendering d'une Vue correspondante. Puisque nous avons une méthode errorAction() dans un ErrorController, c'est le script de Vue /application/views/scripts/error/error.phtml qui sera rendu. Du moins, s'il existe ; créez le donc !

Ah... Si seulement c'était aussi simple...

Retournez dans votre navigateur, et essayez d'appeler l'URL http://helloworld.tld/route/does/not/exist. Du fait que cette URL ne correspond à aucune route (et donc, à aucun Contrôleur), elle devrait entrainer l'apparition du message d'erreur.

7.3. Et bien... Cela n'a pas fonctionné...

A la place de notre page d'erreur, nous avons le véritable message de l'Exception, et ses détails, qui sont tous envoyés au navigateur.

L'ErrorController a été conçu de manière à n'être utilisé que si le Front Controller a été configuré pour ne jamais lever d'exception - sauf pour les exceptions qui doivent être levées parce que quelqu'un a oublié d'ajouter ErrorController ! En fait, l'infameuse Exception à propos du ErrorController non trouvé vient précisément de cette situation. Pour tester l'ErrorController, nous devons effectuer une modification temporaire à /config/application.ini, pour désactiver la levée d'Exceptions par Zend_Controller_Front, en changeant la toute dernière ligne "resources.frontController.throwExceptions", pour lui affecter une valeur de 0 (ce qui signifie false). Cela permet de s'assurer que le plugin ErrorHandler peut servir une page d'erreur libre d'exception, telle que nous l'avons créée.

[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
[Important]Important

N'oubliez pas de revenir à la configuration initiale, valant 1, lorsque nous aurons fini ce chapitre !

Et maintenant, essayons cette URL à nouveau...

7.4. Toutes les Erreurs ne sont pas Egales

Vous pourriez penser que nous pourrions nous arrêter là, et discrêtement échapper à la chambre de torture n°7, mais vous auriez tord. Si vous regardez sous le capot, notre gestionnaire d'erreur est extrêmement vague, et nous répondons toujours aux requêtes HTTP avec une en-tête HTTP/1.1 200 OK. Maintenant, traitez-moi d'idiot si vous en avez envie, mais est-ce que nous n'avons pas admis qu'un problème s'était produit en osant afficher un message d'erreur à l'utilisateur ? Si notre application rencontre une erreur, elle ne devrait absolument jamais retourner un tel code de statut ni un tel message.

Bien que cela puisse ne pas sembler très important, ça l'est. Toute notre application ne sera pas servie à Joe Bloggs via un navigateur. Des requêtes peuvent arriver d'autres applications, qui pourraient faire confiance au code de réponse HTTP, plutôt qu'à la cruauté évidente dont vous faites preuve envers vos développeurs pour qu'ils détectent toutes les erreurs. Dans tous les cas, vous devez penser à votre propre revue interne des applications. Serez-vous en mesure de traquer les problèmes, si toute requête semble réussir, d'après les logs de votre serveur Web ? Il y a aussi le gros problème nommé RFC 2616 "Hypertext Transfer Protocol -- HTTP/1.1", qui décrit la plage de code de statut 2xx en disant : "Cette classe de codes de status indique que la requête émise par le client a été correctement reçue, comprise, et acceptée". En d'autres mots, le W3C a une chambre de torture spéciale, appelée Chambre de Torture de l'Enfer n°1, pour ceux dont les applications croient tout savoir.

Une échec de routage n'est pas une succès, puisqu'il signifie soit que notre fonctionnalité de routage est cassée, soit, et c'est plus probable, que l'utilisateur a requis l'utilisation d'une URL invalide. Puisque la mauvaise URL est une erreur de l'utilisateur, nous ne devrions probablement pas expédier tout de suite notre Team Leader vers la chambre de torture. A la place, nous devrions expliquer (poliment) aux utilisateurs qu'ils ont tord, et inclure une en-tête de statut 404 Not Found, pour le cas où l'utilisateur est une machine. Pour accomplir cela, nous devons ajouter quelques vérifications à notre ErrorController.

Ci-dessus, nous utilisons le Plugin ErrorHandler, et récupérons la valeur de son type d'erreur. Si l'erreur correspond à un problème de routage, nous utiliserons le code statut HTTP 404, qui indique que le problème n'est pas de notre faute, et que le chemin requis n'existe tout simplement pas pour notre application. Sinon, nous utiliserons le code statut HTTP 500, pour toutes les autres erreurs. Le code statut 500 est accompagné du message "Internal Server Error" dans les en-têtes. Maintenant, améliorons notre template d'erreur existant.

De toute évidence, garder nos utilisateurs n'est pas un des premiers objectifs de ce chapitre.

Si vous essayez avec votre navigateur en utilisant une URL invalide, vous devriez obtenir le message d'erreur attendu, ainsi qu'un code statut de 404 dans les en-têtes. Si vous effectuez quelque chose qui entraine la levée d'une Exception différente, vous devriez recevoir en réponse le code status 500, et le message HTML correspondant.

7.5. Conclusion

Gérer les erreurs n'est pas une tâche difficile, mais elle est nécessaire, en particulier quand nos applications peuvent être visitées par d'autres clients, eux-même intégrés à des applications, qui sont dépendant de la communication de codes de statut et d'en-têtes appropriés, et non de pages HTML humainement lisibles, pour déterminer si une requête HTTP a réussi ou non. Il est fortement recommandé que vous vous assuriez que votre application retourne toujours des en-têtes HTTP et codes de statut appropriés. Bien que notre ErrorController semble relativement simple, nous voyons bien que nous pourrions l'améliorer, de manière à logger les erreurs, ou même envoyer des notifications par email.