您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關Laravel異常處理類怎么進行重寫,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
首先我們在app/Exceptions目錄新建一個ExceptionHandler.php繼承自Handler.php
namespace App\Exceptions; class ExceptionHandler extends Handler { }
然后我們在bootstrap/app.php中,使用我們自定義的異常處理類ExceptionHandler替換掉默認的Handler類
//改為我們自定義的ExceptionHandler類 $app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, App\Exceptions\ExceptionHandler::class );
接下來我們就開始重寫渲染方法
在render方法里,我們根據.env文件中的APP_DEBUG來判斷,如果是調試模式,我們還是按照默認方式來渲染錯誤,如果是非調試模式,我們就返回JSON格式的信息
namespace App\Exceptions; use Exception; class ExceptionHandler extends Handler { public function render($request, Exception $exception) { if (env('APP_DEBUG')) { return parent::render($request, $exception); } return response()->json([ 'code' => $exception->getCode(), 'msg' => $exception->getMessage() ]); } }
這樣我們就可以根據APP_DEBUG的值設置是否返回JSON格式的數據了,現在我們把.env的APP_DEBUG的值設為false來測試一下,然后我們故意把代碼寫錯,通過postman或瀏覽器來訪問接口
Route::get('/', function () { //這是一段缺少了分號的代碼,會報異常 echo 'Hello World!' });
在APP_DEBUG=true的情況下還仍然是默認渲染,方便我們查找錯誤排錯
異常類默認會把異常以日志的形式記錄在storage/logs目錄下,并且以laravel-日期(YYYY-MM-DD)命名的形式,.log為后綴保存錯誤日志
我們打開這個日志文件查看記錄的錯誤信息,我們可以發現錯誤信息記錄的非常詳細,除了錯誤說明之外,還記錄了調用棧,如下圖所示
基本上紅框里的信息就夠我們排錯了,不需要像現在這樣記錄的這么詳細,所以要想不記錄調用棧,我們可以重寫report方法
首先我們看一下框架的report方法,代碼在(src/Illuminate/Foundation/Exceptions/Handler.php),我用紅框框起來的代碼就是調用棧信息,我們在重寫這個方法時只需要完全拷貝這個方法里的所有代碼到我們自定義的report方法里,然后把紅框里的代碼去掉即可
我們在我們自定義的異常處理類ExceptionHandler.php中重寫report方法
public function report(Exception $exception) { if ($this->shouldntReport($exception)) { return; } if (Reflector::isCallable($reportCallable = [$exception, 'report'])) { return $this->container->call($reportCallable); } try { $logger = $this->container->make(LoggerInterface::class); } catch (Exception $ex) { throw $exception; } $logger->error( $exception->getMessage() ); }
然后我們再重新請求一下接口再去查看錯誤日志的記錄,可以發現確實沒有記錄調用棧信息了,但是下面的信息還是不夠,我們沒法根據下面的信息判斷錯誤發生在哪一個文件和哪一行,如果能在記錄錯誤信息的時候同時記錄發生錯誤的文件和行就更好了,所以借著修改report方法
public function report(Exception $exception) { if ($this->shouldntReport($exception)) { return; } if (Reflector::isCallable($reportCallable = [$exception, 'report'])) { return $this->container->call($reportCallable); } try { $logger = $this->container->make(LoggerInterface::class); } catch (Exception $ex) { throw $exception; } $logger->error( $exception->getMessage()." at ".$exception->getFile().":".$exception->getLine() ); }
在代碼里我通過exception的getFile()、getLine()方法加上了文件和行數,保存代碼再次訪問接口,查看錯誤日志文件我們可以看到發生錯誤的文件和行數已經記錄下來了,有了這些信息基本我們就可以找到錯誤
截止到這里實現最初的需求我們的ExceptionHandler.php只需要有這些代碼
namespace App\Exceptions; use Exception; use Illuminate\Support\Reflector; use Psr\Log\LoggerInterface; class ExceptionHandler extends Handler { public function render($request, Exception $exception) { if (env('APP_DEBUG')) { return parent::render($request, $exception); } return response()->json([ 'code' => $exception->getCode(), 'msg' => $exception->getMessage() ]); } public function report(Exception $exception) { if ($this->shouldntReport($exception)) { return; } if (Reflector::isCallable($reportCallable = [$exception, 'report'])) { return $this->container->call($reportCallable); } try { $logger = $this->container->make(LoggerInterface::class); } catch (Exception $ex) { throw $exception; } $logger->error( $exception->getMessage()." at ".$exception->getFile().":".$exception->getLine() ); } }
然后還不夠,我們發現剛剛我們把服務器端的錯誤信息以JSON格式返回給客戶端了,這是不允許的,我們應該只把一些客戶端錯誤返回給客戶端,比如密碼不足六位、身份證不合法諸如此類,而服務端出現錯誤時我們只返回給客戶端一個模糊的信息即可,比如“服務器錯誤”,把真實的服務器錯誤信息記錄在日志里面方便開發人員排查錯誤
所以我們需要定義一個客戶端異常專門用戶返回客戶端錯誤,使用如下命令在app/Exceptions目錄下生成一個ClientException.php文件
php artisan make:exception ClientException
修改為構造方法為如下代碼
namespace App\Exceptions; use Exception; class ClientException extends Exception { public function __construct($code, $msg) { parent::__construct($msg, $code); } }
接著我們繼續修改ExceptionHandler.php
namespace App\Exceptions; use Exception; use Illuminate\Support\Reflector; use Psr\Log\LoggerInterface; class ExceptionHandler extends Handler { /** * @var int 錯誤碼 */ protected $code; /** * @var string 錯誤信息 */ protected $message; protected $dontReport = [ ClientException::class ]; public function render($request, Exception $exception) { if ($exception instanceof ClientException) { $this->code = $exception->getCode(); $this->message = $exception->getMessage(); } else { if (env('APP_DEBUG')) { return parent::render($request, $exception); } $this->code = 500; $this->message = '服務器錯誤'; } return response()->json([ 'code' => $this->code, 'msg' => $this->message ]); } public function report(Exception $exception) { if ($this->shouldntReport($exception)) { return; } if (Reflector::isCallable($reportCallable = [$exception, 'report'])) { return $this->container->call($reportCallable); } try { $logger = $this->container->make(LoggerInterface::class); } catch (Exception $ex) { throw $exception; } $logger->error( $exception->getMessage()." at ".$exception->getFile().":".$exception->getLine() ); } }
對于上面的修改做一下說明,laravel的$dontReport屬性的異常類都不會被上報,因為客戶端錯誤信息我們不需要記錄,所以將其添加到$dontReport屬性里,并且在render方法里把異常大概分為了兩大類,一大類就是客戶端異常,另一大類就是服務器異常,我們把服務器異常統一code為500,錯誤信息為服務器錯誤,將真實的錯誤信息記錄在了錯誤日志里,避免把服務器信息暴露給了客戶端。
現在我們來測試我們重寫異常的結果
假如我們想返回客戶端異常,比如沒有權限,這類客戶端異常在錯誤日志里都不會產生記錄,我們本身也不需要記錄
Route::get('/', function () { throw new \App\Exceptions\ClientException(403, '你沒有權限'); });
對于服務器端的錯誤,如少些了分號,客戶端就只會知道服務器的某個接口出了問題,但是不清楚具體問題是什么
Route::get('/', function () { echo 'Hello World!' });
但是真實的錯誤信息會記錄在錯誤日志里,我們仍舊可以通過錯誤日志來修改我們服務端的錯誤
上述就是小編為大家分享的Laravel異常處理類怎么進行重寫了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。