跳到內容

如何自訂錯誤頁面

編輯此頁面

在 Symfony 應用程式中,所有錯誤都被視為例外,無論它們是 404 找不到錯誤,還是由在您的程式碼中拋出某些例外所觸發的嚴重錯誤。

開發環境中,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 錯誤輸出的內容,請建立新的常態化器
  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 方法來覆寫套件內部的範本,並將它們放在 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 格式),或給定狀態碼和格式的錯誤頁面(您可能需要將 https://127.0.0.1/ 替換為您在本機設定中使用的主機)

  • https://127.0.0.1/_error/{statusCode} 適用於 HTML
  • https://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
在某些情況下可能是 nullDebugLoggerInterface 實例。

提示

錯誤頁面預覽也適用於以這種方式設定的您自己的控制器。

使用 kernel.exception 事件

當擲回例外時,HttpKernel 類別會捕捉它並分派 kernel.exception 事件。這讓您有能力以幾種不同的方式將例外轉換為 Response

使用此事件實際上比之前解釋的更強大,但也需要徹底了解 Symfony 內部結構。假設您的程式碼擲回具有特定應用程式領域意義的特殊例外。

針對 kernel.exception 事件編寫您自己的事件監聽器,可讓您更仔細地查看例外,並根據例外採取不同的動作。這些動作可能包括記錄例外、將使用者重新導向到另一個頁面或渲染特殊錯誤頁面。

注意

如果您的監聽器在 ExceptionEvent 事件上呼叫 setResponse(),則傳播將停止,並且回應將傳送至用戶端。

這種方法可讓您建立集中式和分層的錯誤處理:您可以只讓一個(或多個)監聽器處理它們,而不是在各種控制器中一次又一次地捕捉(和處理)相同的例外。

提示

請參閱 ExceptionListener 類別程式碼,以取得此類型進階監聽器的真實範例。此監聽器處理應用程式中擲回的各種安全性相關例外(例如 AccessDeniedException),並採取將使用者重新導向至登入頁面、登出使用者和其他事項等措施。

這份作品,包括程式碼範例,皆以 Creative Commons BY-SA 3.0 授權條款授權。
TOC
    版本