Как настроить страницы ошибок

Дата обновления перевода 2024-07-17

Как настроить страницы ошибок

В приложениях Symfony все ошибки воспринимаются, как исключения, вне зависимости от того, являются ли они простой ошибкой 404 "Не найдено" или фатальной ошибкой, запущенной вызовом какого-то исключения в вашем коде.

В окружении разработки, Symfony ловит все исключения и отображаеть специальную страницу исключений со множеством информации по отладке, чтобы помочь вам быстро обнаружить основную проблему:

Типичная страница исключений в окружении разработки

Так как эти страницы содержат много чувствительной внутренней информации, Symfony не будет отображать её в окружении производства. Вместо этого, она покажет простую и общую ошибку страница ошибки:

Типичная страница ошибки в окружении производства

Страницы ошибок в окружении производства можно настроить разными способами, в зависимости от ваших потребностей:

  1. Если вы просто хотите изменить содержание и стили страниц ошибок так, чтобы они совпадали с остальным вашим приложением, переопределите шаблоны ошибок по умолчанию ;
  2. Если вы хотите изменить содержание вывода ошибки не в HTML, создайте новый нормализатор ;
  3. Если вы также хотите настроить логику, используемую Symfony для генерирования ваших страниц ошибок, то переопределите контроллер ошибок по умолчанию ;
  4. Если вам нужен полный контроль над работой с исключениями, выполните вашу собственную логику - используйте событие the kernel.exception .

Переопределение шаблонов ошибок по умолчанию

Вы можете использовать встроенное средство отображения ошибок Twig, чтобы переопределять шаблоны ошибок по умолчанию. Для этого должны быть установлены как TwigBundle, так и TwigBridge. Выполните эту команду, чтобы убедиться, что они оба установлены:

1
$ composer require symfony/twig-pack

Когда загружается страница ошибки, для отображения шаблона twig и демонастрации пользователю, используется TwigErrorRenderer.

Этот отображатель использует статус-код HTTP, формат запроса и следующую логику, чтобы определить имя файла шаблона:

  1. Ищет шаблон для заданного статус-кода (вроде error500.html.twig);
  2. Если предыдущий шаблон не существует, отбросьте статус-код и ищет общий шаблон ошибок (error.html.twig).

Чтобы переопределить эти шаблоны, просто положитесь на стандартный метод Symfony для пеоепределения шаблонов, живущих внутри пакета: поместите их в каталоге templates/bundles/TwigBundle/Exception/.

Типичный проект, возвращающий страницы HTML и JSON, может выглядеть так:

1
2
3
4
5
6
7
templates/
└─ bundles/
   └─ TwigBundle/
      └─ Exception/
         ├─ error404.html.twig
         ├─ error403.html.twig
         └─ error.html.twig      # Все другие JSON ошибки (включая 500)

Пример шаблона ошибки 404

Чтобы переопределить шаблон ошибки 404 для HTML-страниц, создайте новый шаблон error404.html.twig, находящийся в templates/bundles/TwigBundle/Exception/:

1
2
3
4
5
6
7
8
9
10
11
{# templates/bundles/TwigBundle/Exception/error404.html.twig #}
{% extends 'base.html.twig' %}

{% block body %}
    <h1>Страница не найдена</h1>

    <p>
        Запрошенная страница не найдена. Проверьте опечатки в URL или
        <a href="{{ path('homepage') }}">вернитесь на домашнюю сраницу</a>.
    </p>
{% endblock %}

Если они вам понадобятся, TwigErrorRenderer передаёт некоторую информацию в шаблон ошибок через переменные status_code и status_text, которые хранят HTTP статус-код и сообщение соотвественно.

Tip

Вы можете настроить статус-код, реализовав HttpExceptionInterface и его обязательный метод getStatusCode(). В обратном случае, status_code по умолчанию будет500.

Кроме того, вы имеете доступ к объекту HttpException. через переменнуюTwig exception. Например, если исключение задаёт сообщение (например, с помощью throw $this->createNotFoundException('The product does not exist')), используйте {{ exception.message }} для вывода этого сообщения. Также можно вывести трассировку стека с помощью {{ exception.traceAsString }}, но не стоит делать этого для конечных пользователей, поскольку трассировка содержит конфиденциальные данные.

Tip

PHP-ошибки также по умолчанию превращаются в исключения, поэтому вы также можете получить доступ к деталям этих ошибок, используя exception.

Безопасность и страницы 404

В связи с порядком, в котором загружаются маршрутизация и безопасность, конфиденциальна информация не будет доступна на ваших страницах 404. Это означает, что будет казаться, что ваш пользователь не залогинен в систему на странице 404 (это будет работать при тестировании, но не в производстве).

Тестирование страниц ошибок во время разработки

В то время, как вы находитесь в окружении разработки, Symfony отображает большую страницу исключений вместо вашей новой блестящей страницы ошибок. Так как вам увидеть, как она выглядит изнутри и отладить её?

К счастью, ExceptionController по умолчанию разрешает вам предпросмотр вашей страницы ошибки во время разработки.

Чтобы использовать эту функцию, вам загрузить специальные маршруты, предоставленные TwigBundle (если приложение использует Symfony Flex, то они загружаются автоматически при установке symfony/framework-bundle):

1
2
3
4
5
# config/routes/framework.yaml
when@dev:
    _errors:
        resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
        prefix:   /_error

C добавлением этого маршрута, вы можете использовать такие URL для предпросмотра страницы ошибки для заданного статус-кода в виде HTML или для заданного статус-кода и формата (вам может понадобиться заменить http://localhost/ на хостинг, используемый в ваших локальных настройках):

  • http://localhost/_error/{statusCode} для HTML
  • http://localhost/_error/{statusCode}.{format} для любого другого формата

Переопределение вывода ошибок для не-HTML форматов

Чтобы переопределить не-HTML вывод ошибки, необходимо установить компонент Serializer.

1
$ composer require symfony/serializer-pack

Компонент Serializer имеет встроенный нормализатор FlattenException (ProblemNormalizer) и кодировщики JSON/XML/CSV/YAML. Когда ваше приложение вызывает исключение, Symfony может вывести его в один из этих форматов. Если вы хоите изменить содержание вывода, создайте Нормализатор, который поддерживает ввод FlattenException:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# src/Serializer/MyCustomProblemNormalizer.php
namespace App\Serializer;

use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class MyCustomProblemNormalizer implements NormalizerInterface
{
    public function normalize($exception, string $format = null, array $context = []): array
    {
        return [
            'content' => 'This is my custom problem normalizer.',
            'exception'=> [
                'message' => $exception->getMessage(),
                'code' => $exception->getStatusCode(),
            ],
        ];
    }

    public function supportsNormalization($data, string $format = null, array $context = []): bool
    {
        return $data instanceof FlattenException;
    }
}

Переопределение ExceptionController по умолчаию

Если вам нужно немного больше гибкости кроме простого переопределения шаблона, то вы можете изменить контроллер, отображающий странцу ошибки. Например, вам может быть нужо передать дополнительные переменные в ваш шаблон.

Чтобы сделать это, просто создайте новый контролер где угодно в вашем приложении, и установите опцию конфигурации framework.error_controller , чтобы указать на неё:

1
2
3
# config/packages/framework.yaml
framework:
    error_controller: App\Controller\ErrorController::show

Класс ExceptionListener, используемый TwigBundle в качестве слушателя события kernel.exception, создаёт запрос, который будет развёрнут в вашем контроллере. В дополнение, вашему контроллеру будут переданы два параметра:

exception
Обработка первоначального экземпляра Throwable.
logger
Экемпляр DebugLoggerInterface, который может в некоторых случаях быть null.

Tip

Предпросмотр страницы ошибки также работает с вашими собственными контроллерами, нвстроенными как угодно.

Работа с событием kernel.exception

Когда вызывается исключение, класс HttpKernel ловит его и развёртывает событие kernel.exception. Это даёт вам возможность конвертировать исключение в Response несколькими разными способами.

Работа с этим событием на самом деле значительн более мощная, чем объяснялось раньше, но также требует тщательного понимания внутренних процессов Symfony. Представьте, что ваш код вызывает специальные исключения с конкретным значением в вашем домене приложения.

Написание вашего собственного слушателя событий для события kernel.exception позволяет вам ближе рассмотретьисключение и предпринять по отношению к нему разные действия. Эти действия могут включать в себя запись лога исключения, перенаправление пользователя на другую страницу или отображение специальных страниц ошибки.

Note

Если ваш слушатель вызывает setResponse() в событии ExceptionEvent, распространение будет остановлено и клиенту будет отправлен ответ.

Этот подход позволяет вам создавать централизованную и многослойную обработку ошибок: вместо того, чтобы ловить (и обрабатывать) одни и те же исключения в разных контроллерах снова и снова, вы просто можете иметь одного (или нескольких) слушателей, чтобы разбираться с ними.

Tip

Смотрите код класса ExceptionListener для реального примера продвинутого слушателя такого типа. Этот слушатель обрабатывает различные исключения, связанные с безопасностью, которые вызываются в вашем приложении (как AccessDeniedException) и предпринимает действия вроде перенаправления пользователей на страницу входа, выполняет их вход в систему и др.