第6章 AOP与全局异常处理

Posted haima

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第6章 AOP与全局异常处理相关的知识,希望对你有一定的参考价值。

第6章 AOP与全局异常处理

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 appapicontrollerv1;
10 use appapimodelBanner as BannerModel;
11 use appapivalidateIDMustBePostiveInt;
12 use applibexceptionBannerMissException;
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 appapimodel;
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 applibexception;
10 use Exception;
11 use thinkexceptionHandle;
12 use thinkRequest;
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 applibexception;


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 applibexception;
11 
12 
13 use thinkException;
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 thinkLog;

 

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 applibexception;
10 use Exception;
11 use thinkexceptionHandle;
12 use thinkLog;
13 use thinkRequest;
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

读取applicationconfig.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 applibexception;
10 use Exception;
11 use thinkexceptionHandle;
12 use thinkLog;
13 use thinkRequest;
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与全局异常处理的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 使用 Aop 实现日志全局拦截

第 7 章 异常处理结构代码测试与调试

Spring企业级程序设计 • 第3章 面向切面编程

springmvc 全局异常ControllerAdviceExceptionHandler

springmvc 全局异常ControllerAdviceExceptionHandler

第1章 异常与中断的概念及处理流程