diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index 00cb1b420f8..fa8efef21fc 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -5,32 +5,47 @@ How to Customize Error Pages ============================ -When any exception is thrown in Symfony, the exception is caught inside the -``Kernel`` class and eventually forwarded to a special controller, -``TwigBundle:Exception:show`` for handling. This controller, which lives -inside the core TwigBundle, determines which error template to display and -the status code that should be set for the given exception. +When an exception is thrown, the core ``HttpKernel`` class catches it and +dispatches a ``kernel.exception`` event. This gives you the power to convert +the exception into a ``Response`` in a few different ways. -Error pages can be customized in two different ways, depending on how much -control you need: +The core TwigBundle sets up a listener for this event which will run +a configurable (but otherwise arbitrary) controller to generate the +response. The default controller used has a sensible way of +picking one out of the available set of error templates. -1. Customize the error templates of the different error pages; +Thus, error pages can be customized in different ways, depending on how +much control you need: -2. Replace the default exception controller ``twig.controller.exception:showAction``. +#. :ref:`Use the default ExceptionController and create a few + templates that allow you to customize how your different error + pages look (easy); ` -The default ExceptionController -------------------------------- +#. :ref:`Replace the default exception controller with your own + (intermediate). ` -The default ``ExceptionController`` will either display an +#. :ref:`Use the kernel.exception event to come up with your own + handling (advanced). ` + +.. _use-default-exception-controller: + +Using the Default ExceptionController +------------------------------------- + +By default, the ``showAction()`` method of the +:class:`Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController` +will be called when an exception occurs. + +This controller will either display an *exception* or *error* page, depending on the setting of the ``kernel.debug`` flag. While *exception* pages give you a lot of helpful information during development, *error* pages are meant to be -shown to the end-user. +shown to the user in production. .. sidebar:: Testing Error Pages during Development You should not set ``kernel.debug`` to ``false`` in order to see your - error pages during development. This will also stop + *error* pages during development. This will also stop Symfony from recompiling your twig templates, among other things. The third-party `WebfactoryExceptionsBundle`_ provides a special @@ -38,13 +53,52 @@ shown to the end-user. pages for arbitrary HTTP status codes even with ``kernel.debug`` set to ``true``. -Override Error Templates ------------------------- +.. _`WebfactoryExceptionsBundle`: https://github.com/webfactory/exceptions-bundle + +.. _cookbook-error-pages-by-status-code: + +How the Template for the Error and Exception Pages Is Selected +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -All of the error templates live inside the TwigBundle. To override the -templates, simply rely on the standard method for overriding templates that -live inside a bundle. For more information, see -:ref:`overriding-bundle-templates`. +The TwigBundle contains some default templates for error and +exception pages in its ``Resources/views/Exception`` directory. + +.. tip:: + + In a standard Symfony installation, the TwigBundle can be found at + ``vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle``. In addition + to the standard HTML error page, it also provides a default + error page for many of the most common response formats, including + JSON (``error.json.twig``), XML (``error.xml.twig``) and even + JavaScript (``error.js.twig``), to name a few. + +Here is how the ``ExceptionController`` will pick one of the +available templates based on the HTTP status code and request format: + +* For *error* pages, it first looks for a template for the given format + and status code (like ``error404.json.twig``); + +* If that does not exist or apply, it looks for a general template for + the given format (like ``error.json.twig`` or + ``exception.json.twig``); + +* Finally, it ignores the format and falls back to the HTML template + (like ``error.html.twig`` or ``exception.html.twig``). + +.. tip:: + + If the exception being handled implements the + :class:`Symfony\\Component\\HttpKernel\\Exception\\HttpExceptionInterface`, + the ``getStatusCode()`` method will be + called to obtain the HTTP status code to use. Otherwise, + the status code will be "500". + +Overriding or Adding Templates +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To override these templates, simply rely on the standard method for +overriding templates that live inside a bundle. For more information, +see :ref:`overriding-bundle-templates`. For example, to override the default error template, create a new template located at @@ -74,82 +128,161 @@ template located at .. tip:: - If you're not familiar with Twig, don't worry. Twig is a simple, powerful - and optional templating engine that integrates with Symfony. For more - information about Twig see :doc:`/book/templating`. + If you're not familiar with Twig, don't worry. Twig is a simple, + powerful and optional templating engine that integrates with + Symfony. For more information about Twig see :doc:`/book/templating`. -In addition to the standard HTML error page, Symfony provides a default error -page for many of the most common response formats, including JSON -(``error.json.twig``), XML (``error.xml.twig``) and even JavaScript -(``error.js.twig``), to name a few. To override any of these templates, just -create a new file with the same name in the -``app/Resources/TwigBundle/views/Exception`` directory. This is the standard -way of overriding any template that lives inside a bundle. +This works not only to replace the default templates, but also to add +new ones. -.. _cookbook-error-pages-by-status-code: +For instance, create an ``app/Resources/TwigBundle/views/Exception/error404.html.twig`` +template to display a special page for 404 (page not found) errors. +Refer to the previous section for the order in which the +``ExceptionController`` tries different template names. -Customizing the 404 Page and other Error Pages ----------------------------------------------- +.. tip:: -You can also customize specific error templates according to the HTTP status -code. For instance, create a -``app/Resources/TwigBundle/views/Exception/error404.html.twig`` template to -display a special page for 404 (page not found) errors. + Often, the easiest way to customize an error page is to copy it from + the TwigBundle into ``app/Resources/TwigBundle/views/Exception`` and + then modify it. -Symfony uses the following algorithm to determine which template to use: +.. note:: -* First, it looks for a template for the given format and status code (like - ``error404.json.twig``); + The debug-friendly exception pages shown to the developer can even be + customized in the same way by creating templates such as + ``exception.html.twig`` for the standard HTML exception page or + ``exception.json.twig`` for the JSON exception page. + +.. _custom-exception-controller: -* If it does not exist, it looks for a template for the given format (like - ``error.json.twig``); +Replacing the Default ExceptionController +------------------------------------------ + +If you need a little more flexibility beyond just overriding the +template, then you can change the controller that renders the error +page. For example, you might need to pass some additional variables into +your template. + +.. caution:: -* If it does not exist, it falls back to the HTML template (like - ``error.html.twig``). + Make sure you don't lose the exception pages that render the helpful + error messages during development. + +To do this, simply create a new controller and set the +:ref:`twig.exception_controller ` option +to point to it. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + twig: + exception_controller: AcmeFooBundle:Exception:showException + + .. code-block:: xml + + + + + + + AcmeFooBundle:Exception:showException + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('twig', array( + 'exception_controller' => 'AcmeFooBundle:Exception:showException', + // ... + )); .. tip:: - To see the full list of default error templates, see the - ``Resources/views/Exception`` directory of the TwigBundle. In a - standard Symfony installation, the TwigBundle can be found at - ``vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle``. Often, the easiest way - to customize an error page is to copy it from the TwigBundle into - ``app/Resources/TwigBundle/views/Exception`` and then modify it. + You can also set up your controller as a service. -.. note:: + The default value of ``twig.controller.exception:showAction`` refers + to the ``showAction`` method of the ``ExceptionController`` + described previously, which is registered in the DIC as the + ``twig.controller.exception`` service. - The debug-friendly exception pages shown to the developer can even be - customized in the same way by creating templates such as - ``exception.html.twig`` for the standard HTML exception page or - ``exception.json.twig`` for the JSON exception page. +Your controller will be passed two parameters: ``exception``, +which is a :class:`\\Symfony\\Component\\Debug\\Exception\\FlattenException` +instance created from the exception being handled, and ``logger``, +an instance of :class:`\\Symfony\\Component\\HttpKernel\\Log\\DebugLoggerInterface` +(which may be ``null``). -.. _`WebfactoryExceptionsBundle`: https://github.com/webfactory/exceptions-bundle +.. tip:: -Replace the default Exception Controller ----------------------------------------- + The Request that will be dispatched to your controller is created + in the :class:`Symfony\\Component\\HttpKernel\\EventListener\\ExceptionListener`. + This event listener is set up by the TwigBundle. -If you need a little more flexibility beyond just overriding the template -(e.g. you need to pass some additional variables into your template), -then you can override the controller that renders the error page. +You can, of course, also extend the previously described +:class:`Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController`. +In that case, you might want to override one or both of the +``showAction`` and ``findTemplate`` methods. The latter one locates the +template to be used. -The default exception controller is registered as a service - the actual -class is ``Symfony\Bundle\TwigBundle\Controller\ExceptionController``. +.. caution:: -To do this, create a new controller class and make it extend Symfony's default -``Symfony\Bundle\TwigBundle\Controller\ExceptionController`` class. + As of writing, the ``ExceptionController`` is *not* part of the + Symfony API, so be aware that it might change in following releases. -There are several methods you can override to customize different parts of how -the error page is rendered. You could, for example, override the entire -``showAction`` or just the ``findTemplate`` method, which locates which -template should be rendered. +.. _use-kernel-exception-event: -To make Symfony use your exception controller instead of the default, set the -:ref:`twig.exception_controller ` option -in app/config/config.yml. +Working with the kernel.exception Event +----------------------------------------- + +As mentioned in the beginning, the ``kernel.exception`` event is +dispatched whenever the Symfony Kernel needs to +handle an exception. For more information on that, see :ref:`kernel-kernel.exception`. + +Working with this event is actually much more powerful than what has +been explained before but also requires a thorough understanding of +Symfony internals. + +To give one example, assume your application throws +specialized exceptions with a particular meaning to your domain. + +In that case, all the default ``ExceptionListener`` and +``ExceptionController`` could do for you was trying to figure out the +right HTTP status code and display your nice-looking error page. + +:doc:`Writing your own event listener ` +for the ``kernel.exception`` event allows you to have a closer look +at the exception and take different actions depending on it. Those +actions might include logging the exception, redirecting the user to +another page or rendering specialized error pages. + +.. note:: + + If your listener calls ``setResponse()`` on the + :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent`, + event propagation will be stopped and the response will be sent to + the client. + +This approach allows you to create centralized and layered error +handling: Instead of catching (and handling) the same exceptions +in various controllers again and again, you can have just one (or +several) listeners deal with them. .. tip:: - The customization of exception handling is actually much more powerful - than what's written here. An internal event, ``kernel.exception``, is thrown - which allows complete control over exception handling. For more - information, see :ref:`kernel-kernel.exception`. + To see an example, have a look at the `ExceptionListener`_ in the + Security Component. + + It handles various security-related exceptions that are thrown in + your application (like :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException`) + and takes measures like redirecting the user to the login page, + logging them out and other things. + +Good luck! + +.. _`ExceptionListener`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php