如何自訂錯誤頁面
在 Symfony 應用程式中,所有錯誤都被視為例外,無論它們是 404 找不到錯誤,還是由在您的程式碼中拋出某些例外所觸發的嚴重錯誤。
在開發環境中,Symfony 會捕捉所有例外並顯示特殊的例外頁面,其中包含大量除錯資訊,以協助您找出根本問題

由於這些頁面包含許多敏感的內部資訊,Symfony 不會在生產環境中顯示它們。相反地,它會顯示一個簡潔且通用的錯誤頁面

生產環境的錯誤頁面可以根據您的需求以不同的方式自訂
- 如果您只想變更錯誤頁面的內容和樣式,使其與應用程式的其餘部分相符,請覆寫預設錯誤範本;
- 如果您想變更非 HTML 錯誤輸出的內容,請建立新的常態化器;
- 如果您也想調整 Symfony 用於產生錯誤頁面的邏輯,請覆寫預設錯誤控制器;
- 如果您需要完全控制例外處理以執行您自己的邏輯,請使用 kernel.exception 事件。
覆寫預設錯誤範本
您可以使用內建的 Twig 錯誤渲染器來覆寫預設錯誤範本。必須安裝 TwigBundle 和 TwigBridge 才能使用此功能。執行此命令以確保兩者都已安裝
1
$ composer require symfony/twig-pack
當錯誤頁面載入時,會使用 TwigErrorRenderer 來渲染 Twig 範本以顯示給使用者。
此渲染器使用 HTTP 狀態碼和以下邏輯來判斷範本檔案名稱
- 尋找給定狀態碼的範本(例如
error500.html.twig
); - 如果先前的範本不存在,請捨棄狀態碼並尋找通用錯誤範本(
error.html.twig
)。
若要覆寫這些範本,請依賴標準 Symfony 方法來覆寫套件內部的範本,並將它們放在 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 格式),或給定狀態碼和格式的錯誤頁面(您可能需要將 https://127.0.0.1/
替換為您在本機設定中使用的主機)
https://127.0.0.1/_error/{statusCode}
適用於 HTMLhttps://127.0.0.1/_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;
}
}
覆寫預設 ErrorController
如果您需要比僅覆寫範本更大的彈性,則可以變更渲染錯誤頁面的控制器。例如,您可能需要將一些額外的變數傳遞到您的範本中。
若要執行此操作,請在應用程式中的任何位置建立一個新的控制器,並設定 framework.error_controller 組態選項以指向它
1 2 3
# config/packages/framework.yaml
framework:
error_controller: App\Controller\ErrorController::show
FrameworkBundle 使用的 ErrorListener 類別作為 kernel.exception
事件的監聽器,會建立將分派到您控制器的請求。此外,您的控制器將傳遞兩個參數
exception
- 正在處理的原始 Throwable 實例。
logger
- 在某些情況下可能是
null
的 DebugLoggerInterface 實例。
提示
錯誤頁面預覽也適用於以這種方式設定的您自己的控制器。
使用 kernel.exception
事件
當擲回例外時,HttpKernel 類別會捕捉它並分派 kernel.exception
事件。這讓您有能力以幾種不同的方式將例外轉換為 Response
。
使用此事件實際上比之前解釋的更強大,但也需要徹底了解 Symfony 內部結構。假設您的程式碼擲回具有特定應用程式領域意義的特殊例外。
針對 kernel.exception
事件編寫您自己的事件監聽器,可讓您更仔細地查看例外,並根據例外採取不同的動作。這些動作可能包括記錄例外、將使用者重新導向到另一個頁面或渲染特殊錯誤頁面。
注意
如果您的監聽器在 ExceptionEvent 事件上呼叫 setResponse()
,則傳播將停止,並且回應將傳送至用戶端。
這種方法可讓您建立集中式和分層的錯誤處理:您可以只讓一個(或多個)監聽器處理它們,而不是在各種控制器中一次又一次地捕捉(和處理)相同的例外。
提示
請參閱 ExceptionListener 類別程式碼,以取得此類型進階監聽器的真實範例。此監聽器處理應用程式中擲回的各種安全性相關例外(例如 AccessDeniedException),並採取將使用者重新導向至登入頁面、登出使用者和其他事項等措施。