錯誤處理

簡介

當你開始一個新的 Laravel 項目時,它已經配置了錯誤和異常處理機制。 App\Exceptions\Handler 類別用於記錄應用觸發的所有異常,然後將其呈現給用戶。我們將在本章節深入討論這個類別

設定

config/app.php 設定檔案中的 debug 選項決定了對於一個錯誤實際上將顯示多少訊息給用戶。預設情況下,該選項的設置將按照在 .env 檔案中的 APP_DEBUG 環境變數的值

對於本地開發,你應該將 APP_DEBUG 環境變數的值設置為 true。在正式環境中,該值應始終為 false。如果在正式環境中將該值設置為 true,可能會將敏感設定值暴露給應用的用戶

異常處理

報告異常

所有異常都是由 App\Exceptions\Handler 類別處理。此類包含一個 register() ,可以在其中註冊自定義異常報告程序和渲染器回呼。本文將詳細說明每個概念。異常報告用於記錄異常或將其發送到如 Flare、Bugsnag 或 Sentry 等外部服務。預設情況下,將根據你的日誌配置來記錄異常。不過你可以用自己喜歡的方式來記錄異常

例如,如果您需要使用不同的方式來報告不同的異常,可以使用 reportable() 來註冊一個 Closure,當需要報告指定的異常的時候便會執行它。 Laravel 將通過檢查 Closure 的型別提示來判斷 Closure 報告的異常類型

use App\Exceptions\InvalidOrderException;

/**
 * Register the exception handling callbacks for the application.
 *
 * @return void
 */
public function register()
{
    $this->reportable(function (InvalidOrderException $e) {
        //
    });
}

當使用 reportable() 註冊一個自定義異常報告回呼函式時, Laravel 依然會使用預設的日誌配置記錄下應用異常。 如果您想要在預設的日誌堆疊中停止這個行為,可以在定義報告回呼時使用 stop()

$this->reportable(function (InvalidOrderException $e) {
    //
})->stop();

$this->reportable(function (InvalidOrderException $e) {
    return false;
});

技巧:

要為指定的異常自定義異常報告,可以使用可報告異常

全域日誌 Context

在可用的情況下, Laravel 會自動將當前用戶的編號作為資料添加到每一條異常日誌資料中。你可以通過複寫 App\Exceptions\Handler 類別中的 context() 來定義您自己的全域 Context(環境變數),此後每一條異常日誌訊息都將包含這個訊息

/**
 * Get the default context variables for logging.
 *
 * @return array
 */
protected function context()
{
    return array_merge(parent::context(), [
        'foo' => 'bar',
    ]);
}

reporter 幫助函式

有時你可能需要報告異常,但不終止當前請求的處理。 report() 幫助函數允許你在不渲染錯誤頁面給用戶的情況下快速報告異常:

public function isValid($value)
{
    try {
        // Validate the value...
    } catch (Throwable $e) {
        report($e);

        return false;
    }
}

按類型忽略異常

當建立應用時,常會有些類型的異常是你想要忽略掉而不需要報告的。應用的異常處理器的 $dontReport 屬性包含了一個不會被日誌記錄的異常類型的陣列,它預設是一個空陣列,任何類別被加到這個陣列後將不會被回報。比方說由 404 錯誤導致的錯誤就不會被寫到日誌中,你可以根據需要添加其他異常類型到該陣列中

use App\Exceptions\InvalidOrderException;

/**
 * A list of the exception types that should not be reported.
 *
 * @var array
 */
protected $dontReport = [
    InvalidOrderException::class,
];

在背後, Laravel 已經為你忽略了一些類型的錯誤,比方說 404 的 HTTP not found 錯誤,或者是由無效的 CSRF tokens 所造成的 419 HTTP 回應等

渲染異常

預設情況下, Laravel 異常處理器會自動為你轉換異常為 HTTP 回應。你當然也可以在異常處理器的 renderable() 中註冊一個特定類型的異常的自定義渲染 Closure 來實現。

傳給 renderable() 的 Closure 應該要回傳一個 Illuminate\Http\Response 實例,它將會經由 response() 來生成。 Laravel 將會根據 Closure 的型別提示來確定異常的類別:

use App\Exceptions\InvalidOrderException;

/**
 * Register the exception handling callbacks for the application.
 *
 * @return void
 */
public function register()
{
    $this->renderable(function (InvalidOrderException $e, $request) {
        return response()->view('errors.invalid-order', [], 500);
    });
}

Reportable & Renderable 異常

除了在異常處理器的 report 和 render 方法中檢查異常類型外,你也可以直接在自定義異常中定義 report 和 render 方法。當定義了這些方法之後它們將被框架自動呼叫

<?php

namespace App\Exceptions;

use Exception;

class InvalidOrderException extends Exception
{
    /**
     * Report the exception.
     *
     * @return void
     */
    public function report()
    {
        //
    }

    /**
     * Render the exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function render($request)
    {
        return response(...);
    }
}

如果您的異常包含了僅在滿足某些條件時才報告的自定義報告邏輯,您可能需要指示 Laravel 在某些狀況下只使用預設的異常處理配置去報告它。你可以在異常的 report() 中回傳 false 來達成:

/**
 * Report the exception.
 *
 * @return bool|void
 */
public function report()
{
    // Determine if the exception needs custom reporting...

    return false;
}

技巧:

你可以通過型別提示傳遞任意的依賴到 report() 中,它們將被 Laravel 的服務容器自動注入到方法中

HTTP 異常

某些錯誤碼描述了服務器的 HTTP 錯誤原因。例如,錯誤404代表「頁面未找到」,錯誤401代表「未經授權的錯誤」甚至是開發者造成的 500 內部錯誤。要在應用的任何地方生成這樣的回應,可以使用 abort() 幫助函式

abort(404);

自定義 HTTP 錯誤頁面

Laravel 創建了可以輕鬆顯示各種 HTTP 狀態碼的自定義錯誤頁面。例如如果您想要自定義 HTTP 狀態碼為 404 的錯誤頁面,您只需創建 resources/views/errors/404.blade.php 檔案。該檔案用於應用產生的所有 404 錯誤。errors資料夾中的視圖名稱應為它們所對應的 HTTP 狀態碼。由 abort() 引發的 HttpException 實例將作為 $exception 變數傳遞給視圖:

<h2>{{ $exception->getMessage() }}</h2>

你可以使用 vendor:publish 這一個 Artisan 命令來發布 Laravel 錯誤頁面模板到專案中。一旦你發布了模板,你就可以根據需要來自定義它們

php artisan vendor:publish --tag=laravel-errors

Select a repo