如何自定义错误页面
在 Symfony 应用程序中,所有错误都被视为异常,无论是 404 Not Found 错误还是由于在代码中抛出某些异常而触发的致命错误。
在开发环境中,Symfony 会捕获所有异常并显示一个特殊的异常页面,其中包含大量调试信息,以帮助您发现根本问题

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

可以根据您的需求,通过不同的方式自定义生产环境的错误页面
- 如果您只想更改错误页面的内容和样式,使其与应用程序的其余部分相匹配,请覆盖默认的错误模板;
- 如果您想更改非 HTML 错误输出的内容,请创建一个新的 normalizer;
- 如果您还想调整 Symfony 用于生成错误页面的逻辑,请覆盖默认的错误控制器;
- 如果您需要完全控制异常处理以运行您自己的逻辑,请使用 kernel.exception 事件。
覆盖默认错误模板
您可以使用内置的 Twig 错误渲染器来覆盖默认的错误模板。TwigBundle 和 TwigBridge 都需要安装才能实现此功能。运行此命令以确保两者都已安装
1
$ composer require symfony/twig-pack
当错误页面加载时,TwigErrorRenderer 用于渲染 Twig 模板以向用户显示。
此渲染器使用 HTTP 状态码和以下逻辑来确定模板文件名
- 查找给定状态码的模板(例如
error500.html.twig
); - 如果之前的模板不存在,则放弃状态码并查找通用错误模板 (
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_code
和 status_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),并采取措施,例如将用户重定向到登录页面、注销用户以及其他操作。