Documentation officielle du framework PHP Symfony 3

Comment faire pour personnaliser les pages retournées en cas d'erreur ?

Symfony

Traducteur

Guillaume SIGUI

La gestion des erreurs est un élément important dans une application Web. Avoir des messages suffisamment explicatifs sur vos pages d'erreur peut apporter une plus-value à votre application, et à l'expérience utilisateur. Symfony propose des pages d'erreur par défaut, selon les événements, mais il est possible de les personnaliser.

Dans ce tutoriel, vous apprendre à personnaliser les pages d'erreur dans votre application Web Symfony.

Un espace vous est proposé sur le forum pour apporter vos avis sur ce tutoriel.

Commentez

- Titre : Documentation officielle du framework PHP Symfony 3

- Auteur : Symfony

- Traducteur : Guillaume SIGUI

- Parution : 6 décembre 2016

- Public visé :

- Licence : Le contenu de cet article est rédigé par Symfony et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- I. Introduction

- II. Remplacement des templates d'erreur par défaut

- II-A. Présentation générale

- II-B. Exemple avec le template du code d'erreur 404

- II-C. Tests des pages d'erreur pendant la programmation

- III. Remplacement du ExceptionController par défaut

- IV. Programmer avec l'événement kernel.exception

- Notes de la rédaction de Developpez.com

I - Introduction

Dans les applications Symfony, toutes les erreurs sont traitées comme des exceptions. Peu importe si ce sont juste des erreurs « 404 Objet non trouvé » ou une erreur fatale déclenchée par une exception dans votre code.

Dans l'environnement de programmation, Symfony détecte toutes les erreurs et affiche une page d'exception spéciale avec plusieurs informations de débogage pour vous aider à retrouver rapidement le problème racine.

Et comme ces pages contiennent plusieurs informations internes sensibles, Symfony ne les affiche pas dans un environnement de production. En lieu et place de cela, il va afficher une simple page d'erreur générique.

Les pages d'erreur en environnement de production peuvent être personnalisées de plusieurs façons selon vos besoins :

1. Si vous voulez juste modifier le contenu et le style de vos pages d'erreur pour correspondre avec le reste de votre application, il suffit de remplacer les templates d'erreur par défaut ;

2. Si vous souhaitez également modifier la logique utilisée par Symfony pour générer des pages d'erreur, remplacer le contrôleur de gestion des exceptions par défaut ;

3. Si vous avez besoin d'un contrôle total de la gestion des exceptions pour exécuter votre propre logique, utiliser l'événement kernel.exception.

II - Remplacement des templates d'erreur par défaut

II-A - Présentation générale

Quand une page d'erreur est en train d'être chargée, un contrôleur interne ExceptionController est utilisé pour retourner un template Twig présenté à l'utilisateur.

Ce contrôleur utilise un statut de code HTTP et un format de requête dans la logique suivante pour déterminer le nom du template :

1. Retrouver un template pour le format indiqué et le code du statut (par exemple error404.json.twig ou error500.html.twig) ;

2. Si le précédent template n'existe pas, il faut ignorer le code de statut pour retrouver un template générique selon le format indiqué (par exemple error.json.twig ou error.xml.twig) ;

3. Si aucun des templates précédents n'existe, il faut revenir au template générique HTML (error.html.twig).

Pour remplacer ces templates, il suffit d'utiliser la méthode standard de Symfony pour remplacer les templates contenus dans un bundle : les mettre dans le répertoire app/Resources/TwigBundle/views/Exception.

Un projet typique qui retourne des pages JSON et HTML ressemble à peu près à ceci :

1. app/

2. └─ Resources/

3. └─ TwigBundle/

4. └─ views/

5. └─ Exception/

6. ├─ error404.html.twig

7. ├─ error403.html.twig

8. ├─ error.html.twig      # All other HTML errors (including 500)

9. ├─ error404.json.twig

10. ├─ error403.json.twig

11. └─ error.json.twig      # All other JSON errors (including 500)

II-B - Exemple avec le template du code d'erreur 404

Pour remplacer un template d'erreur 404 dans les pages HTML, créer un nouveau template error404.html.twig dans le répertoire app/Resources/TwigBundle/views/Exception/.

1. {# app/Resources/TwigBundle/views/Exception/error404.html.twig #}

2. {% extends 'base.html.twig' %}

4. {% block body %}

5. <h1>Page not found</h1>

7. {% if is_granted('IS_AUTHENTICATED_FULLY') %}

8. {# ... #}

9. {% endif %}

11. <p>

12. The requested page couldn't be located. Checkout for any URL

13. misspelling or <a href="{{ path('homepage') }}">return to the homepage</a>.

14. </p>

15. {% endblock %}

Dans le cas où vous avez besoin d'eux, le ExceptionController passe certaines informations au template d'erreur via les variables status_code et status_text qui stockent respectivement le code et le message du statut HTTP.

Vous pouvez personnaliser le code du statut en implémentant l'interface HttpExceptionInterface et sa méthode getStatusCode() requise. Sinon le status_code est par défaut à 500.

Les pages d'exception présentées dans l'environnement de programmation peuvent être personnalisées de la même manière que les pages d'erreur. Il faut créer un nouveau template exception.html.twig pour la page standard de l'exception HTML ou exception.json.twig pour la page de l'exception JSON.

II-C - Tests des pages d'erreur pendant la programmation

Tant que vous êtes dans l'environnement de programmation, Symfony vous montre une grande page d'exception au lieu de votre nouvelle brillante page d'erreur personnalisée. Donc, comment voir à quoi elle ressemble et comment la déboguer ?

Heureusement, le contrôleur, par défaut, ExceptionController vous permet de prévisualiser vos pages d'erreur pendant la phase de développement.

Pour utiliser cette fonction, vous avez besoin d'une définition semblable à celle-ci dans le fichier routing_dev.yml :

Script YAML

1. # app/config/routing_dev.yml

2. _errors:

3. resource: "@TwigBundle/Resources/config/routing/errors.xml"

4. prefix:   /_error

Script XML

1. <!-- app/config/routing_dev.xml -->

2. <?xml version="1.0" encoding="UTF-8" ?>

3. <routes xmlns="http://symfony.com/schema/routing"

4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

5. xsi:schemaLocation="http://symfony.com/schema/routing

6. http://symfony.com/schema/routing/routing-1.0.xsd">

8. <import resource="@TwigBundle/Resources/config/routing/errors.xml"

9. prefix="/_error" />

10. </routes>

Script PHP

1. // app/config/routing_dev.php

2. use Symfony\Component\Routing\RouteCollection;

4. $collection = new RouteCollection();

5. $collection->addCollection(

6. $loader->import('@TwigBundle/Resources/config/routing/errors.xml')

7. );

8. $collection->addPrefix("/_error");

10. return $collection;

Si vous venez d'une ancienne version de Symfony, vous auriez besoin d'ajouter cela dans votre fichier routing_dev.yml. Si vous démarrez de zéro, l'édition standard de Symfony contient déjà cela pour vous.

Avec cette route ajoutée, vous pouvez utiliser les URL comme ceci :

1. http://localhost/app_dev.php/_error/{statusCode}

2. http://localhost/app_dev.php/_error/{statusCode}.{format}

Ces URL vous permettent de prévisualiser la page d'erreur relative au code de statut donné en HTML ou dans le code du statut et du format donné.

III - Remplacement du ExceptionController par défaut

Si vous avez besoin d'un peu plus de flexibilité, au-delà d'un simple remplacement de template, alors vous pouvez changer le contrôleur qui retourne la page d'erreur. Par exemple, vous pourriez avoir besoin de passer certaines variables à votre template.

Pour le faire, il suffit de créer simplement un nouveau contrôleur à n'importe quel emplacement de votre application, et paramétrer l'option de configuration twig.exception_controller pour pointer dessus :

Script YAML

1. # app/config/config.yml

2. twig:

3. exception_controller:  AppBundle:Exception:showException

Script XML

1. <!-- app/config/config.xml -->

2. <?xml version="1.0" encoding="UTF-8" ?>

3. <container xmlns="http://symfony.com/schema/dic/services"

4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

5. xmlns:twig="http://symfony.com/schema/dic/twig"

6. xsi:schemaLocation="http://symfony.com/schema/dic/services

7. http://symfony.com/schema/dic/services/services-1.0.xsd

8. http://symfony.com/schema/dic/twig

9. http://symfony.com/schema/dic/twig/twig-1.0.xsd">

11. <twig:config>

12. <twig:exception-controller>AppBundle:Exception:showException</twig:exception-controller>

13. </twig:config>

14. </container>

Script PHP

1. // app/config/config.php

2. $container->loadFromExtension('twig', array(

3. 'exception_controller' => 'AppBundle:Exception:showException',

4. // ...

5. ));

La classe ExceptionListener utilisée par le TwigBundle comme listener de l'événement kernel.exception crée la requête qui sera envoyée à votre contrôleur. En plus, deux paramètres seront passés à votre contrôleur :

1. exception : une instance FlattenException créée par l'exception gérée ;

2. logger : une instance DebugLoggerInterface qui peut être null dans certaines circonstances.

Au lieu de créer une nouvelle exception de contrôleur à partir de zéro, vous pouvez tout simplement étendre le contrôleur par défaut ExceptionController. Dans ce cas, vous pouvez remplacer l'une ou les deux méthodes suivantes : showAction() et findTemplate(). Cette dernière permet de retrouver le template qui sera utilisé.

Dans le cas où il s'agit d'étendre le contrôleur ExceptionController, vous devez configurer un service pour lui passer l'environnement Twig et le flag debug au constructeur.

Script YAML

1. # app/config/services.yml

2. services:

3. app.exception_controller:

4. class: AppBundle\Controller\CustomExceptionController

5. arguments: ['@twig', '%kernel.debug%']

Script XML

1. <!-- app/config/services.xml -->

2. <?xml version="1.0" encoding="utf-8" ?>

3. <container xmlns="http://symfony.com/schema/dic/services"

4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

5. xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"

6. >

7. <services>

8. <service id="app.exception_controller"

9. class="AppBundle\Controller\CustomExceptionController"

10. >

11. <argument type="service" id="twig"/>

12. <argument>%kernel.debug%</argument>

13. </service>

14. </services>

15. </container>

Script PHP

1. // app/config/services.php

2. use Symfony\Component\DependencyInjection\Reference;

3. use Symfony\Component\DependencyInjection\Definition;

5. $definition = new Definition('AppBundle\Controller\CustomExceptionController', array(

6. new Reference('twig'),

7. '%kernel.debug%'

8. ));

9. $container->setDefinition('app.exception_controller', $definition);

Et maintenant, configurez twig.exception_controller en utilisant le contrôleur comme syntaxe des services (par exemple : app.exception_controller:showAction).

La prévisualisation de la page d'erreur fonctionne aussi pour vos propres contrôleurs configurés de cette façon.

IV - Programmer avec l'événement kernel.exception

Quand une exception est détectée, la classe HttpKernel la récupère et envoie un événement kernel.exception. Cela vous donne la possibilité de convertir l'exception dans un objet « Response » de peu de manières différentes.

Travailler avec cet événement est en fait beaucoup plus puissant que ce qui a été expliqué plus haut, mais requiert une connaissance plus approfondie du fonctionnement interne de Symfony. Cela peut être utile dans un cas où votre code a besoin de lancer des exceptions spécialisées ayant une signification particulière pour votre domaine d'application.

Écrire votre propre listener d'événements pour l'événement kernel.exception vous permettra d'examiner, de plus près, l'exception et exécuter plusieurs actions en dépendant. Ces actions peuvent porter sur la journalisation de l'exception, la redirection des utilisateurs vers une autre page, ou encore le rendu de pages d'erreur spécialisées.

Si votre listener appelle la méthode setResponse() dans l'événement GetResponseForExceptionEvent, la propagation va s'arrêter et l'objet « Response » sera envoyé au client.

Cette approche vous permet de créer une couche de gestion centralisée des erreurs : au lieu de capturer (et gérer) les mêmes exceptions dans divers contrôleurs à plusieurs reprises, vous pouvez juste avoir un (ou seulement très peu) de listeners pour les traiter.

Observez le code de la classe ExceptionListener comme exemple réel de listener avancé de ce type. Ce listener permet de gérer plusieurs exceptions relatives à la sécurité, qui sont intégrées dans votre application (comme AccessDeniedException) et prendre des mesures telles que rediriger l'utilisateur à la page de login, les journaliser et bien d'autres choses encore.

Notes de la rédaction de Developpez.com

Nous remercions Guillaume Sigui pour la traduction de ce tutoriel et f-leb pour la relecture orthographique.