1. 程式人生 > >第6章 AOP與全局異常處理

第6章 AOP與全局異常處理

同名 info trace pos base auth 處理異常 時有 thinkphp

第6章 AOP與全局異常處理

https://coding.imooc.com/learn/list/97.html

6-1 正確理解異常處理流程 13:23

curl+alt+o 快速移除無用的use引入命名空間 靜態調用model層處理業務流程,上面要引入命名空間, model裏文件名字和控制器的名字同名時,引入時上面要as一個別名 技術分享圖片 技術分享圖片 技術分享圖片

6-1-1 全局異常處理要做兩件事情:

  1. 記錄日誌
  2. 做出統一的異常處理

6-2 固有的處理異常的思維模式與流程 14:23

6-2-1 思路:

控制器裏調用model層驗證異常,model層如果拋出異常,catch就會捕捉到並拋出異常

技術分享圖片
model裏拋出異常
技術分享圖片

運行結果

技術分享圖片

重要: api處理的決對不能返回一個錯誤html的頁面,要返回錯的json信息

6-3 理清思路,總結異常的分類_ 06:33

技術分享圖片

6-4 實現自定義全局異常處理 上 17:04

調用model層

技術分享圖片

model裏拋出異常

技術分享圖片

嘗試所有的異常都拋出~~~~~~

ExceptionHandler.php裏render()方法的$ex就會捕捉到並拋出異常

技術分享圖片

配置裏設置為自定義異常處理方法

技術分享圖片

運行結果

<hr>
技術分享圖片 =====================================================================================

定義基礎異常

技術分享圖片

定義BannerMissException異常,extends BaseException 技術分享圖片

error_code錯誤碼

 1     999 未知錯誤
 2     1 開頭為通用錯誤
 3
2 商品類錯誤 4 3 主題類錯誤 5 4 Banner類錯誤 6 5 類目類錯誤 7 6 用戶類錯誤 8 8 訂單類錯誤 9 10 10000 通用參數錯誤 11 10001 資源未找到 12 10002 未授權(令牌不合法) 13 10003 嘗試非法操作(自己的令牌操作其他人數據) 14 10004 授權失敗(第三方應用賬號登陸失敗) 15 10005 授權失敗(服務器緩存異常) 16 17 18 20000 請求商品不存在 19 20 30000 請求主題不存在 21 22 40000 Banner不存在 23 24 50000 類目不存在 25 26 60000 用戶不存在 27 60001 用戶地址不存在 28 29 80000 訂單不存在 30 80001 訂單中的商品不存在,可能已被刪除 31 80002 訂單還未支付,卻嘗試發貨 32 80003 訂單已支付過

6-5 實現自定義全局異常處理 下 16:04

技術分享圖片

6-5-1 測試思路:

6-5-1.1 調用model

v1/Banner.php裏的getBanner裏調用model層裏的getBannerById(), 判斷如果返回的為空,就拋出BannerMissException異常,拋出後,ExceptionHandler.php/render(Exception $e)就會接收到這個異常(所以執出的異常,exceptionHandler都會捕捉到,由它判斷是不是屬於異常的類型,而自定義異常都繼承自BaseException異常,所以判斷這個$e的異常屬於是否BaseException自定義的異常,如果是就會出拋出自定義的異常處理.否則就拋出是服務器異常. 但要拋出這個自定義異常,首先BannerMissException要繼承自Exception類,BaseException.php裏繼承自Exception類.BannerMissException又繼承自BaseException.所以滿足條件,沒有問題

技術分享圖片

 1 <?php
 2 /**
 3  * Created by PhpStorm.
 4  * User: Haima
 5  * Date: 2018/7/8
 6  * Time: 15:58
 7  */
 8 
 9 namespace app\api\controller\v1;
10 use app\api\model\Banner as BannerModel;
11 use app\api\validate\IDMustBePostiveInt;
12 use app\lib\exception\BannerMissException;
13 
14 class Banner
15 {
16     /**
17      * 獲取指定id的banner信息
18      * @url /banner/:id
19      * @http GET
20      * @id banner的id號
21      */
22     public function getBanner($id)
23     {
24 
25         (new IDMustBePostiveInt())->goCheck(); //驗證$id是否為正整數
26         $banner = BannerModel::getBannerById($id);//調用model
27         if (!$banner){
28            throw new BannerMissException(); //判斷結果不存在,拋出異常
29         }
30 //        return $banner;
31     }
32 }

6-5-1.2 model層裏驗證數據返回null

return null; 技術分享圖片
 1     <?php
 2     /**
 3     * Created by PhpStorm.
 4     * User: Hiama
 5     * Date: 2018/7/12
 6     * Time: 1:16
 7     */
 8      
 9     namespace app\api\model;
10     class Banner
11     {
12         public static function getBannerById($id){
13             return null;
14         }
15     }

6-5-1.3 exceptionHandler.php判斷異常類型,並拋出相應的異常

exceptionHandler.php裏的render()方法判斷Banner.php控制器拋出的是什麽異常. Banner.php控制器裏拋出了BannerMissException異常,BannerMissException又繼承自BaseException, 判斷出$e最終屬於BaseException異常,就會拋出自定義異常. ExceptionHandler裏獲取BannerMissException裏定義的code,msg和errorCode,

所以ExceptionHandler最終就會拋出BannerMissException裏定義的code,msg和errorCode信息

技術分享圖片

技術分享圖片

 1 <?php
 2 /**
 3  * Created by PhpStorm.
 4  * User: Haima
 5  * Date: 2018/7/12
 6  * Time: 3:11
 7  */
 8 
 9 namespace app\lib\exception;
10 use Exception;
11 use think\exception\Handle;
12 use think\Request;
13 
14 class ExceptionHandler extends Handle
15 {
16     private $code;
17     private $msg;
18     private $errorCode;
19 
20     public function render(Exception $e)
21     {
22         if($e instanceof BaseException){
23             $this->code = $e->code;
24             $this->msg = $e->msg;
25             $this->errorCode = $e->errorCode;
26         }
27         else{
28             $this->code = 500;
29             $this->msg = ‘服務器錯誤,不想給你看‘;
30             $this->errorCode = 999;
31         }
32         $request = Request::instance();
33         $result = array(
34             ‘msg‘  => $this->msg,
35             ‘error_code‘ => $this->errorCode,
36             ‘request_url‘  => $request->url()
37         );
38         return json($result,$this->code);
39     }
40 }

技術分享圖片

<?php
/**
 * Created by Haima.
 * Author:Haima
 * QQ:228654416
 * Date: 2018/7/12
 * Time: 20:48
 */

namespace app\lib\exception;


class BannerMissException extends BaseException
{
    public $code =  404;

    public $msg = ‘請求的Banner不存在‘;

    public $errrCode = 40000;
}

技術分享圖片

 1 <?php
 2 /**
 3  * Created by Haima.
 4  * Author:Haima
 5  * QQ:228654416
 6  * Date: 2018/7/12
 7  * Time: 20:49
 8  */
 9 
10 namespace app\lib\exception;
11 
12 
13 use think\Exception;
14 
15 class BaseException extends Exception
16 {
17     //HTTP 狀態碼 404,200
18     public $code = 400;
19 
20     //錯誤的具體信息
21     public $msg = ‘參數錯誤‘;
22 
23     //自定義的錯誤碼
24     public $errorCode = 10000;
25 }

6-5-1.4 處理的結果:

技術分享圖片

6-6 ThinkPHP5中的日誌系統 14:34

6-6.1 thinkphp5定義常量的位置

技術分享圖片

技術分享圖片

6-6.2 thinkphp5定義日誌的位置

  在根目錄的runtime/log/ 文件夾下面

  技術分享圖片

  修改thinkphp生成日誌的位置   在public/index.php裏添加

  define(‘LOG_PATH‘, __DIR__ . ‘/../log/‘); //修改thinkphp生成日誌的位置

  技術分享圖片

6-7 在全局異常處理中加入日誌記錄 09:13

  具體可以參考手冊裏的說明:

  https://www.kancloud.cn/manual/thinkphp5/118127

  6-7.1 可以關閉日誌寫入 

1 ‘log‘   => [
2       // 可以關閉日誌寫入
3       ‘type‘  => ‘test‘,
4   ],

  6-7.2 默認的話記錄的日誌級別是INFO,也可以指定日誌級別:

    Log::record(‘測試日誌信息,這是警告級別‘,‘notice‘);

  

日誌級別

ThinkPHP對系統的日誌按照級別來分類,並且這個日誌級別完全可以自己定義,系統內部使用的級別包括:

  • log 常規日誌,用於記錄日誌
  • error 錯誤,一般會導致程序的終止
  • notice 警告,程序可以運行但是還不夠完美的錯誤
  • info 信息,程序輸出信息
  • debug 調試,用於調試信息
  • sql SQL語句,用於SQL記錄,只在數據庫的調試模式開啟時有效

系統提供了不同日誌級別的快速記錄方法,例如:  

1 Log::error(‘錯誤信息‘);
2 Log::info(‘日誌信息‘);
3 // 和下面的用法等效
4 Log::record(‘錯誤信息‘,‘error‘);
5 Log::record(‘日誌信息‘,‘info‘);

還封裝了一個助手函數用於日誌記錄,例如:

1 trace(‘錯誤信息‘,‘error‘);
2 trace(‘日誌信息‘,‘info‘);

自定義生成日誌的例子:

config.php裏關閉了系統自動生成日誌

技術分享圖片

修改thinkphp生成日誌的位置

在public/index.php裏

  define(‘LOG_PATH‘, __DIR__ . ‘/../log/‘); //修改thinkphp生成日誌的位置

  技術分享圖片

控制器裏臨時拋出服務器異常

技術分享圖片

ExceptionHandler裏封裝服務器異常recordErrorLog() 生成日誌的函數

ExceptionHandler裏判斷拋出的異常是服務器異常,走else裏的代碼,並調用封裝的recordErrorLog() 生成日誌的函數,寫日誌

因為config.php裏關閉了系統自動寫日誌,所以在recordErrorLog() 函數裏要初始化一下日誌

Log::record() 記錄日誌信息到內存 上面需要引入 use think\Log;

Log::record(‘測試日誌信息,這是警告級別‘,‘notice‘);

技術分享圖片

 1 <?php
 2 /**
 3  * Created by PhpStorm.
 4  * User: Haima
 5  * Date: 2018/7/12
 6  * Time: 3:11
 7  */
 8 
 9 namespace app\lib\exception;
10 use Exception;
11 use think\exception\Handle;
12 use think\Log;
13 use think\Request;
14 
15 class ExceptionHandler extends Handle
16 {
17     private $code;
18     private $msg;
19     private $errorCode;
20 
21     public function render(Exception $e)
22     {
23         if($e instanceof BaseException){
24             $this->code = $e->code;
25             $this->msg = $e->msg;
26             $this->errorCode = $e->errorCode;
27         }
28         else{
29             $this->code = 500;
30             $this->msg = ‘服務器錯誤,不想給你看‘;
31             $this->errorCode = 999;
32             $this->recordErrorLog($e);  //調用服務器異常錯誤
33         }
34         $request = Request::instance();
35         $result = array(
36             ‘msg‘  => $this->msg,
37             ‘error_code‘ => $this->errorCode,
38             ‘request_url‘  => $request->url()
39         );
40         return json($result,$this->code);
41     }
42 
43   //服務器異常錯誤
44   private function recordErrorLog(Exception $e){
45         Log::init([
46             ‘type‘=>‘File‘, //生成的類型是文件
47             ‘path‘=>LOG_PATH, //日誌生成的路徑
48             ‘level‘=>[‘error‘] // 日誌記錄級別,使用數組表示
49         ]);
50         Log::record($e->getMessage(),‘error‘); //寫入日誌
51     }
52 }

給控制器發送請求:

技術分享圖片

此時已經在項目根目錄裏自動成功日誌目錄了

技術分享圖片


6-8 全局異常處理的應用 上 25:23

讀取application\config.php配置文件裏內容的方法

方法一:

  config(‘app_debug‘)

方法二:

  Config::get(‘app_debug‘);

註意:

配置文件裏的內容只能讀取它裏的某些配置信息,不能用它來做數據保存,

如果要保存數據可以寫入數據庫,redis緩存,thinkphp自提緩存,或者其它緩存的地方,或者保存到全局變量裏

做全局異常處理,前/後臺開發人員分別顯示報錯的方式

前端人員顯示報錯的頁面,後端人員顯示報錯的json信息

思路:

在ExceptionHandler.php裏的服務器異常裏做判斷,

判斷配置裏debug是否為true(把它做為一個開關,通常上線後會關閉debug調適,當然你也可以自定義一個開關),

如果debug為ture就顯示tp5框架自身的報錯頁面,給前臺開發人員看,

否則就顯示json的報錯信息並寫入日誌中,給後端的開發人員看.

控制器裏臨時拋出服務器異常

技術分享圖片

ExceptionHandler.php裏讀取debug值做為寫日誌信息的開關判斷

技術分享圖片

 1 <?php
 2 /**
 3  * Created by PhpStorm.
 4  * User: Haima
 5  * Date: 2018/7/12
 6  * Time: 3:11
 7  */
 8 
 9 namespace app\lib\exception;
10 use Exception;
11 use think\exception\Handle;
12 use think\Log;
13 use think\Request;
14 
15 class ExceptionHandler extends Handle
16 {
17     private $code;
18     private $msg;
19     private $errorCode;
20 
21     public function render(Exception $e)
22     {
23         if($e instanceof BaseException){
24             //如果是自定義異常,則控制http狀態碼,不需要記錄日誌
25             //因為這些通常是因為客戶端傳遞參數錯誤或者是用戶請求造成的異常
26             //不應當記錄日誌
27             $this->code = $e->code;
28             $this->msg = $e->msg;
29             $this->errorCode = $e->errorCode;
30         }
31         else{
32             // 如果是服務器未處理的異常,將http狀態碼設置為500,並記錄日誌
33             //Config::get(‘app_debug‘); //獲取config.php裏的配置信息
34             if (config(‘app_debug‘)){  //獲取config.php裏的配置信息如果是true走這裏
35                 // 如果是前臺調適人員看就顯示json格式錯誤
36                 // 調試狀態下需要顯示TP默認的異常頁面,因為TP的默認就是頁面
37                 // 很容易看出問題
38                 return parent::render($e); //調用thinkphp5默認的報錯頁面
39             }
40             //如果是後臺調適人員看就顯示json格式錯誤
41             $this->code = 500;
42             $this->msg = ‘服務器錯誤,不想給你看‘;
43             $this->errorCode = 999;
44             $this->recordErrorLog($e);  //調用服務器異常錯誤
45         }
46         $request = Request::instance();
47         $result = array(
48             ‘msg‘  => $this->msg,
49             ‘error_code‘ => $this->errorCode,
50             ‘request_url‘  => $request->url()
51         );
52         return json($result,$this->code);
53     }
54 
55     //服務器異常錯誤
56     private function recordErrorLog(Exception $e){
57         Log::init([
58             ‘type‘=>‘File‘, //生成的類型是文件
59             ‘path‘=>LOG_PATH, //日誌生成的路徑
60             ‘level‘=>[‘error‘] // 日誌記錄級別,使用數組表示
61         ]);
62         Log::record($e->getMessage(),‘error‘);  //寫入日誌
63     }
64 }

為false時:寫入json日誌

‘app_debug‘ => false,

訪問:

技術分享圖片

已經寫入日誌中:

技術分享圖片

為true時:顯示報錯頁面,並不會寫入自定義的log目錄的日誌中

‘app_debug‘ => true,

訪問: 技術分享圖片

自定義的異常不顯示頁面報錯

自定義的異常就顯示我們自定義的json形式的報錯就可以了,沒有必要再顯示頁面的報錯了(沒有意義),所以就不用再做if判斷了,


6-9 全局異常處理的應用 中 12:04


6-10 全局異常處理的應用 下 04:16


6-11 本章小結與AOP思想 12:56

第6章 AOP與全局異常處理