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