[ Laravel從入門到精通系列 ] 請求與響應系列 —— 異常處理篇:概述
前面我們介紹 Laravel 中 HTTP 請求生命週期和中介軟體底層處理邏輯的時候,都涉及到了vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
這個檔案,對應的Illuminate\Foundation\Http\Kernel
類是我們 Laravel 應用 HTTP Kernel 類App\Http\Kernel
類的基類,該類中有一個用於處理 HTTP 請求的handle
方法,這段程式碼也是應用核心入口處理邏輯所在:
public function handle($request) { try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); $response = $this->renderException($request, $e); } catch (Throwable $e) { $this->reportException($e = new FatalThrowableError($e)); $response = $this->renderException($request, $e); } $this->app['events']->dispatch( new Events\RequestHandled($request, $response) ); return $response; }
在將使用者請求例項傳入sendRequestThroughRouter
方法進行處理並返回響應的整個過程中,都有外層包裹的try...catch
程式碼塊對處理過程中的任何異常進行捕獲和處理:
$this->reportException($e); $response = $this->renderException($request, $e);
正如你所看到的,請求處理過程中的所有未處理異常都會被捕獲並按照以下兩步進行處理:
- 根據應用配置向不同的渠道報告異常;
- 將異常轉化為可渲染的格式並以響應的方式返回給終端使用者。
以上兩個步驟底層執行邏輯分別對應異常處理器的report 方法和render 方法 。
Laravel 預設的異常處理器位於app/Exceptions/Handler.php
,對應的處理器類繼承自基類Illuminate\Foundation\Exceptions\Handler
,如果你需要對異常處理邏輯進行自定義的話,可以在這裡覆蓋父類的report
方法和render
方法實現,關於異常報告和渲染的具體細節,我們放到後面單獨去展開,這裡我們先對 Laravel 異常處理的整體思路有一個大致的瞭解。
熟悉 PHP 異常處理邏輯的同學可能知道,PHP 提供了設定錯誤報告級別函式error_reporting
以及預設的異常處理器函式set_exception_handler
,不太熟悉的同學可以先參考這篇教程。Laravel 也是遵循這一思路來實現異常處理的,對應的初始化邏輯位於Illuminate\Foundation\Bootstrap\HandleExceptions
裡面的bootstrap
方法中:
public function bootstrap(Application $app) { $this->app = $app; error_reporting(-1); set_error_handler([$this, 'handleError']); set_exception_handler([$this, 'handleException']); register_shutdown_function([$this, 'handleShutdown']); if (! $app->environment('testing')) { ini_set('display_errors', 'Off'); } }
Laravel 預設設定的錯誤報告級別是-1
,即儘可能顯示所有 PHP 錯誤,包括將來 PHP 可能加入的新的錯誤級別和常量,預設的異常錯誤處理器是當前例項的handleError
方法,預設的異常處理器是當前例項的handleException
方法:
public function handleException($e) { if (! $e instanceof Exception) { $e = new FatalThrowableError($e); } try { $this->getExceptionHandler()->report($e); } catch (Exception $e) { // } if ($this->app->runningInConsole()) { $this->renderForConsole($e); } else { $this->renderHttpResponse($e); } }
仍然是首先呼叫App\Exceptions\Handler
的report
方法報告異常,然後根據控制檯應用還是 Web 應用分別呼叫對應的渲染方法渲染異常。
以上HandleExceptions
例項的bootstrap
方法會在呼叫sendRequestThroughRouter
方法時在應用啟動期間呼叫:
public function bootstrap() { if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); } }
這樣,在處理中介軟體和請求業務邏輯時就可以通過註冊的異常處理器來處理系統異常了。當然,如果你對 Laravel 提供的預設異常處理器處理邏輯不太滿意,或者不符合你的需求,可以通過在bootstrap/app.php
中指定繫結自定義的、實現了Illuminate\Contracts\Debug\ExceptionHandler
介面的異常處理器到容器來完成自定義系統預設異常處理器的設定工作:
$app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, App\Exceptions\Handler::class );
當前預設的異常處理器是我們前面提到的App\Exceptions\Handler
類。有關 Laravel 異常處理器異常報告和渲染的更多細節和自定義實現,學院君接下來分兩篇的篇幅分別探討。
另外,Laravel 控制檯應用異常處理邏輯和上面介紹的 HTTP 應用是一樣的,你可以自行從入口檔案app/Console/Kernel.php
的handle
方法開始去分析。