基于AOP思想,构建thinkphp5的项目全局异常处理层

异常总结

抛出异常

手动抛出异常

可以使用\think\Exception类来抛出异常

// 使用think自带异常类抛出异常
throw new \think\Exception('异常消息', 100006);

也可以使用系统提供的助手函数来简化处理:

exception('异常消息', 100006);
// 使用自定义异常类
exception('异常消息', 100006, \foobar\Exceeption);

抛出 HTTP 异常

可以使用\think\exception\HttpException类来抛出异常

// 抛出 HTTP 异常
throw new \think\exception\HttpException(404, '异常消息', null, [参数]);

系统提供了助手函数abort简化HTTP异常的处理,例如:

abort(404, '异常消息', [参数])

thinkphp异常处理

框架自带的异常处理

什么都不做,抛出的异常会被thinphp框架的全局异常处理层处理。

自定义异常处理

异常处理接管

框架支持异常页面由开发者自定义类进行处理,需要在config.php文件中配置参数exception_handle

// 异常处理handle类 留空使用 \think\exception\Handle
    'exception_handle'       => 'app\lib\exception\ExceptionHandler',

自定义类需要继承Handle并且实现render方法,这样抛出的异常会流入到自定义异常处理类的render方法中。

可以参考如下代码:

<?php
namespace app\lib\exception;

use Exception;
use think\exception\Handle;
use think\exception\HttpException;
class Http extends Handle
{

    public function render(Exception $e)
    {
        // 参数验证错误
        if ($e instanceof ValidateException) {
            return json($e->getError(), 422);
        }

        // 请求异常
        if ($e instanceof HttpException && request()->isAjax()) {
            return response($e->getMessage(), $e->getStatusCode());
        }
        
        //TODO::开发者对异常的操作
        //可以在此交由系统处理
        return parent::render($e);
    }

}

日志总结

1、thinkphp框架会自动将日志写入runtime文件夹中 ,日志配置数据在config.php文件中


'log' => [
        // 日志记录方式,内置 file socket 支持扩展
        'type'  => 'file',
        // 日志保存目录
        'path'  => LOG_PATH,
        // 日志记录级别
        'level' => [],
    ]

手动记录

一般情况下,系统的日志记录是自动的,无需手动记录,但是某些时候也需要手动记录日志信息,Log类提供了3个方法用于记录日志。

use think\Log;

Log::record();	//记录日志信息到内存

Log::save();	//把保存在内存中的日志信息(用指定的记录方式)写入

Log::write();	//实时写入一条日志信息

由于系统在请求结束后会自动调用Log::save方法,所以通常,你只需要调用Log::record记录日志信息即可。

record方法用法如下:

Log::record('测试日志信息');
//默认的话记录的日志级别是INFO,也可以指定日志级别:

Log::record('测试日志信息,这是警告级别','notice');
//采用record方法记录的日志信息不是实时保存的,如果需要实时记录的话,可以采用write方法,例如:

Log::write('测试日志信息,这是警告级别,并且实时写入','notice');

项目开发时异常处理总结

项目中异常处理开发一般为:捕获异常->写入日志->返回客户端就他信息,具体可以分为一下两种类型进行处理。

异常处理分类:

(1)、由于用户行为造成的异常(没有通过检验器,没有查询到结果):捕获异常->返回用户具体错误信息。(不需要写入日志)

(2)、服务器自身异常(代码错误,调用外部接口错误):捕获异常->写入日志->返回用户信息(不需要告诉用户具体错误信息)

所以我们可以根据实际情况自定义异常处理类,构建属于自己项目的异常处理层,流程如下
1、由于用户行为造成的异常
(1)、构建项目异常处理层

构建BaseException基础异常信息类模板

<?php
namespace app\lib\exception;
use think\Exception;
use Throwable;

class BaseException extends Exception
{
    //HTTP 状态码 404,200
    public $code = 400;
    //错误具体信息
    public $msg = '参数错误';
    //自定义的错误码
    public $errorCode = 10000;
    //构造函数
    public function __construct($params=[])
    {
        if(!is_array($params)){
            return ;
        }
        if(array_key_exists('code',$params)){
            $this->code=$params['code'];
        }
        if(array_key_exists('msg',$params)){
            $this->msg=$params['msg'];
        }
        if(array_key_exists('errorCode',$params)){
            $this->errorCode=$params['errorCode'];
        }
    }

}


自定义不同异常的信息模板,基础BaseException类

namespace app\lib\exception;

class ParameterException extends BaseException
{
    public $code=400;
    public $msg='参数错误';
    public $errorCode=10000;

}

####自定义全局异常处理类

分析上面的两类异常情况:

(1)、有用户行为导致的异常,一般这种异常时需要我们自己进行捕获的,并且将错误的原因返回给客户端。

(2)、系统自身错误,我们也不知道哪里出的错,所以一般不是我们人为捕获的,所以异常一般是框架自身捕获的异常,

所以我们可以根据错误信息模板是否符合BaseException基础异常信息类模板,来进行区分处理。

代码如下:

namespace app\lib\exception;

use Exception;
use think\exception\Handle;
use think\Log;
use think\Request;

class ExceptionHandler extends Handle
{
    private $code;
    private $msg;
    private $errorCode;
    //需要返回客户端请求路径
    public function render(Exception $e)
    {
        if ($e instanceof BaseException){
            //如果是自定义的异常
            $this->code=$e->code;
            $this->msg=$e->msg;
            $this->errorCode=$e->errorCode;
        } else{
            if(config('log_debug')){
                return parent::render($e);
            }else{
                $this->code=500;
                $this->msg='服务器内部错误';
                $this->errorCode=999;
                $this->recordErrorLog($e);
            }


        }
        $request=Request::instance();
        $result=[
            'msg'=>$this->msg,
            'error_code'=>$this->errorCode,
            'request_url'=>$request->url(),
        ];
        return json($result,$this->code);

    }
    public function recordErrorLog(Exception $e){
        Log::init([
            'type'=>'file',
            'path'=>LOG_PATH,
            'level'=>['error']
        ]);
        Log::record($e->getMessage(),'error');
    }
}


根据不同类型进行异常处理
(1)有用户行为导致的异常,如检查到http请求参数id为非整数,这个就会返回一个统一的json

public function getBanner($id){
        (new IDMustBePostiveInt())->goCheck();

        $banner=Banner::getBannerByID($id);
        if (!$banner){
            //使用自定义的一个
            throw new \app\lib\exception\ParameterException([
            'msg'=>"id必须为整数"
]);
        }

        return $banner;
    }

(2)服务器自身错误,这个是框架提供的错误信息,会处理后返回给可以一个统一的‘服务器错误’,信息。并经错误信息保存写入日志,供研发人员进行错误查找。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值