Skip to content

Improve cookbook entry for error pages in 2.3~ #4294

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 17, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
285 changes: 209 additions & 76 deletions cookbook/controller/error_pages.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,100 @@
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); <use-default-exception-controller>`

The default ExceptionController
-------------------------------
#. :ref:`Replace the default exception controller with your own
(intermediate). <custom-exception-controller>`

The default ``ExceptionController`` will either display an
#. :ref:`Use the kernel.exception event to come up with your own
handling (advanced). <use-kernel-exception-event>`

.. _use-default-exception-controller:

Using the Default ExceptionController
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Customize Error Templates

I like including the numbers when you have a mini-table of contents like we do here. In fact, I really like how you organized things. We could even link the above lines down to each section.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you show me an example for such in-page links please?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! If the header were Customize Error Templates, then you would have:

See `Customize Error Templates`_

-------------------------------------

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
test controller that allows you to display your custom error
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"including [...] to name a few" seems incorrect to me (not a native though).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept that sentence unchanged.


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
Expand Down Expand Up @@ -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 <config-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

<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:twig="http://symfony.com/schema/dic/twig"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">

<twig:config>
<twig:exception-controller>AcmeFooBundle:Exception:showException</twig:exception-controller>
</twig:config>
</container>

.. 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 <config-twig-exception-controller>` option
in app/config/config.yml.
Working with the kernel.exception Event
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would enclose kernel.exception in literals.

-----------------------------------------

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 </cookbook/service_container/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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nicely explained


.. 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't the default one have the lowest-possible priority? If so, I don't think we need the first part of this note.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it has a low priority (meaning that registering your listener with the default priority of 0 will make it run before the core one). But it is not the lowest-possible priority (it is possible to build lower integers in PHP, as we are still far from - PHP_INT_MAX)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure - there's nothing special in the TwigBundle config, so I'd say it's the "default" priority and your own one must be better than that (i. e. non-default)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mpdude It's an event subscriber - so it's inside the class - it's -128. So if you don't do anything, your listener will be called first :).


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!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! :D


.. _`ExceptionListener`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php