任何程序还是框架异常处理是难点和重点之一。今天学习thinkPHP5 框架的同时,对面向对象框架的通过AOP切面思想封装异常处理层有了初步理解,在此记录一下 。
一、异常按照作用分为两类
1、业务异常:非代码问题,犹豫用户的输入和数据处理过程中而开发人员产生的异常,需要将异常原因和处理结果返回展示给用户,一般不需要记录日志。对系统的稳定性和流畅性没有影响,可以预测异常的产生,一般能够确定明显的原因,
2、系统异常:一般是代码或者逻辑原因产生的异常,无法预测,原因不明确,属于系统bug。这种异常不需要给展示给用户,只提示“服务异常”等提示就可以。具体异常内容和环境状况需要记录到日志,一般后续开发人员查看并排查。对系统的稳定性产生影响。
二、面向对象异常特性
1、一般语言都有异常类Exception,自定义异常都需要继承自该Exception异常类
2、一般框架都有抛出异常的类,需要重写该抛出异常的类来自定义抛出异常
3、抛出异常是按照冒泡规则向上级逐步传递异常
class BannerModel
{
public static function getBannerById($id)
{
// 底层:在model类里面抛出异常
try {
1 / 0;
}catch (Exception $exception) {
throw $exception;
}
return 'this is banner ';
}
}
Model类里对按抛出一个异常,可以在调用model类的controller类二次处理异常(包括日志记录,保存现场数据等),也可以不捕获,直接在model类抛出
class BannerController
{
function getBanner($id)
{
(new IdMustBePositiveInt())->goCheck();
try{
$banner = BannerModel::getBannerById($id);
}catch(Exception $e){
// 捕获model类里抛出的异常,做其他的操作或者直接抛出
throw $;
}
return $banner;
}
}
三、thinkPHP 5 封装自定义异常处理层
1、自定义BaseException类,该类继承与框架的Exception并且初始化自定义异常抛出所需要的参数。一般api接口会初始化三个参数,code代表状态码,通过http状态返回,msg作为具体异常信息抛出,err_Code是具体异常所属的系统自定义的异常代码。
class BaseException extends Exception
{
// http状态码
public $code = 400;
// 错误具体信息
public $mgs = '参数错误';
// 自定义错误码
public $errCode = 10000;
}
2、编写每一个具体情况的自定义异常。该异常类继承自前面定义的BaseException,并且重写三个抛出异常所需要的参数,表示当前异常的信息。下面是一个Banner不存在异常
class BannerMissException extends BaseException
{
// http状态码
public $code = 404;
public $mgs = '请求的轮播图不存在';
// 系统所定义的异常代码
public $errCode = 40000;
}
3、重写抛出异常的函数类。将render函数重写,自定义抛出异常。将异常按照前面的分类,分成两种种类。如果一个异常是我们自定义编写的异常属于业务异常,自定义业务异常格式化以后按照api标注输出给客户端。系统异常按照格式化以后跟环境信息一起保存到系统日志,一遍排查处理。
class ExceptionHandler extends Handle
{
private $code;
private $msg;
private $errCode;
// 返回客户端当前请求的url路径
public function render(Exception $e)
{
if ($e instanceof BaseException) {
// 如果是自定义异常,需要传给客户端
$this->code = $e->code;
$this->msg = $e->mgs;
$this->errCode = $e->errCode;
}else{
// 系统是异常
// 开发模式下抛出默认异常页面,错误信息比较详细,方便调试
if(config('app_debug')){
return parent::render($e);
}
// 非开发模式,抛出自定义异常给客户端
$this->code = 500;
$this->msg = '服务器内部错误,请联系开发人员';
$this->errCode = 999;
// 同时将异常情况记录到日志
$this->recordErrorLog($e);
}
$request = Request::instance();
$result = [
'msg' => $this->msg,
'error_code' => $this->errCode,
'request_url' => $request->url()
];
return json($result, $this->code); // TODO: Change the autogenerated stub
}
private function recordErrorLog(Exception $e) {
Log::init([
'type' => 'File',
'path' => LOG_PATH,
'level' => ['error']
]);
Log::record($e->getMessage(), 'error');
}
}
这样就封装好了一个异常自定义异常处理框架。如果在代码中抛出的是像BannerMissException这种异常是我们自定义的异常,会抛出给客户端。其余我们没有定义过的异常一律按照系统异常抛出并记录到日志。