跳到内容

如何自定义错误页面

编辑此页

在 Symfony 应用程序中,所有错误都被视为异常,无论是 404 Not Found 错误还是由于在代码中抛出某些异常而触发的致命错误。

开发环境中,Symfony 会捕获所有异常并显示一个特殊的异常页面,其中包含大量调试信息,以帮助您发现根本问题

A typical exception page in the development environment with the full stacktrace and log information.

由于这些页面包含许多敏感的内部信息,Symfony 不会在生产环境中显示它们。相反,它会显示一个最小化且通用的错误页面

A typical error page in the production environment.

可以根据您的需求,通过不同的方式自定义生产环境的错误页面

  1. 如果您只想更改错误页面的内容和样式,使其与应用程序的其余部分相匹配,请覆盖默认的错误模板
  2. 如果您想更改非 HTML 错误输出的内容,请创建一个新的 normalizer
  3. 如果您还想调整 Symfony 用于生成错误页面的逻辑,请覆盖默认的错误控制器
  4. 如果您需要完全控制异常处理以运行您自己的逻辑,请使用 kernel.exception 事件

覆盖默认错误模板

您可以使用内置的 Twig 错误渲染器来覆盖默认的错误模板。TwigBundle 和 TwigBridge 都需要安装才能实现此功能。运行此命令以确保两者都已安装

1
$ composer require symfony/twig-pack

当错误页面加载时,TwigErrorRenderer 用于渲染 Twig 模板以向用户显示。

此渲染器使用 HTTP 状态码和以下逻辑来确定模板文件名

  1. 查找给定状态码的模板(例如 error500.html.twig);
  2. 如果之前的模板不存在,则放弃状态码并查找通用错误模板 (error.html.twig)。

要覆盖这些模板,请依赖标准的 Symfony 方法来覆盖 bundle 内部的模板,并将它们放在 templates/bundles/TwigBundle/Exception/ 目录中。

一个典型的返回 HTML 页面的项目可能如下所示

1
2
3
4
5
6
7
templates/
└─ bundles/
   └─ TwigBundle/
      └─ Exception/
         ├─ error404.html.twig
         ├─ error403.html.twig
         └─ error.html.twig      # All other HTML errors (including 500)

404 错误模板示例

要覆盖 HTML 页面的 404 错误模板,请在 templates/bundles/TwigBundle/Exception/ 中创建一个新的 error404.html.twig 模板

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>Page not found</h1>

    <p>
        The requested page couldn't be located. Checkout for any URL
        misspelling or <a href="{{ path('homepage') }}">return to the homepage</a>.
    </p>
{% endblock %}

如果您需要它们,TwigErrorRenderer 通过 status_codestatus_text 变量将一些信息传递给错误模板,这些变量分别存储 HTTP 状态码和消息。

提示

您可以通过实现 HttpExceptionInterface 及其必需的 getStatusCode() 方法来自定义异常的状态码。否则,status_code 将默认为 500

此外,您可以通过 exception Twig 变量访问 HttpException 对象。例如,如果异常设置了消息(例如,使用 throw $this->createNotFoundException('The product does not exist')),请使用 {{ exception.message }} 打印该消息。您还可以使用 {{ exception.traceAsString }} 输出堆栈跟踪,但不要为最终用户这样做,因为跟踪包含敏感数据。

提示

默认情况下,PHP 错误也会转换为异常,因此您也可以使用 exception 访问这些错误详细信息。

安全 & 404 页面

由于路由和安全加载的顺序,安全信息在您的 404 页面上不可用。这意味着在 404 页面上,您的用户看起来像是已注销(在测试时可以工作,但在生产环境中不行)。

在开发期间测试错误页面

当您处于开发环境时,Symfony 会显示大的异常页面,而不是您闪亮的新自定义错误页面。那么,您如何查看它的外观并对其进行调试呢?

幸运的是,默认的 ErrorController 允许您在开发期间预览您的错误页面。

要使用此功能,您需要加载 FrameworkBundle 提供的一些特殊路由(如果应用程序使用 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

添加此路由后,您可以使用如下 URL 预览给定状态码的错误页面(HTML 格式)或给定状态码和格式的错误页面(其他格式)(您可能需要将 http://127.0.0.1/ 替换为您本地设置中使用的主机)

  • http://127.0.0.1/_error/{statusCode} (HTML 格式)
  • http://127.0.0.1/_error/{statusCode}.{format} (任何其他格式)

覆盖非 HTML 格式的错误输出

要覆盖非 HTML 错误输出,需要安装 Serializer 组件。

1
$ composer require symfony/serializer-pack

Serializer 组件具有内置的 FlattenException normalizer (ProblemNormalizer) 和 JSON/XML/CSV/YAML 编码器。当您的应用程序抛出异常时,Symfony 可以以这些格式之一输出它。如果您想更改输出内容,请创建一个新的 Normalizer,该 Normalizer 支持 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;
    }
}

覆盖默认 ErrorController

如果您需要比仅仅覆盖模板更大的灵活性,那么您可以更改渲染错误页面的控制器。例如,您可能需要在模板中传递一些额外的变量。

为此,请在应用程序中的任何位置创建一个新的控制器,并将 framework.error_controller 配置选项设置为指向它

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

FrameworkBundle 用作 kernel.exception 事件侦听器的 ErrorListener 类创建将分派到您的控制器的请求。此外,您的控制器将传递两个参数

exception
正在处理的原始 Throwable 实例。
logger
一个 DebugLoggerInterface 实例,在某些情况下可能为 null

提示

错误页面预览也适用于您以这种方式设置的自己的控制器。

使用 kernel.exception 事件

当抛出异常时,HttpKernel 类会捕获它并分派一个 kernel.exception 事件。这使您能够以几种不同的方式将异常转换为 Response

使用此事件实际上比之前解释的要强大得多,但也需要彻底理解 Symfony 内部原理。假设您的代码抛出了具有特定应用程序领域含义的专用异常。

kernel.exception 事件编写您自己的事件侦听器,您可以更仔细地查看异常,并根据它采取不同的操作。这些操作可能包括记录异常、将用户重定向到另一个页面或渲染专门的错误页面。

注意

如果您的侦听器在 ExceptionEvent 事件上调用 setResponse(),则传播将被停止,并且响应将发送到客户端。

这种方法允许您创建集中式和分层式的错误处理:您不必在各种控制器中一次又一次地捕获(和处理)相同的异常,而可以只让一个(或多个)侦听器来处理它们。

提示

有关此类高级侦听器的真实示例,请参阅 ExceptionListener 类代码。此侦听器处理应用程序中抛出的各种与安全相关的异常(例如 AccessDeniedException),并采取措施,例如将用户重定向到登录页面、注销用户以及其他操作。

本作品,包括代码示例,根据 Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本