正确理解异常处理流程
新建Banner模型
applicationapimodelBanner.php
class Banner
{
public static function getBannerById($id)
{
//根据id获取banner信息
return 'this is banner info';
}
}
使用
applicationapicontrollerv1Banner.php
use appapimodelBanner as BannerModel;
class Banner
{
public function getBanner($id)
{
$validate = new IDMustBePositiveInt();
$validate->goCheck();
$banner = BannerModel::getBannerByID($id)
return $banner;
}
}
固有的处理异常的思维模式与流程
applicationapimodelBanner.php
class Banner
{
public static function getBannerById($id)
{
try{
1 / 0;
}catch (Exception $ex) {
throw $ex; //使用tp5全局异常
}
return 'this is banner info';
}
}
开启调试模式以查看服务器返回的错误详细信息
applicationconfig.php
return [
'app_debug' => true
];
此时调用接口将返回
用json对象的形式返回
applicationapicontrollerv1Banner.php
<?php
namespace appapicontrollerv1;
use appapimodelBanner as BannerModel;
use appapivalidateIDMustBePositiveInt;
use thinkException;
class Banner
{
public function getBanner($id)
{
(new IDMustBePositiveInt())->goCheck();
try {
$banner = BannerModel::getBannerByID($id);
} catch (Exception $ex) {
$err = [
'error_code' => 10001,
'msg ' => $ex->getMessage()
];
//转成json
//指明状态码
return json($err, 400);
}
}
}
查看结果
自定义全局异常处理
对tp5异常处理类进行重写
applicationconfig.php
return [
'exception_handle'=>'applibexceptionExceptionHandler',
];
applicationlibexceptionExceptionHandler.php
class ExceptionHandler extends Handle
{
}
统一描述错误
applicationlibexceptionBaseException.php
class BaseException extends Exception
{
public $code = 400; //状态码
public $msg = '参数错误'; // 错误信息
public $errorCode = 10000; //自定义错误码
}
applicationlibexceptionBannerMissException.php
class BannerMissException extends BaseException
{
public $code = 404;
public $msg = "请求的Banner不存在";
public $errorCode = 40000;
}
applicationlibexceptionExceptionHandler.php
class ExceptionHandler extends Handle
{
public function render(Exception $e)
{
return json('~~~~') //先进行一次测试,看看异常是否都会进入
}
}
测试,重写成功!
自定义异常
applicationlibexceptionExceptionHandler.php
class ExceptionHandler extends Handle
{
private $code;
private $msg;
private $errorCode;
//注意这里使用的是Exception
public function render(Exception $e){
if($e instanceof BaseException) {
//如果是自定义异常,则控制http状态码,不需要记录日志
//因为这些通常是因为客户端传递参数错误或者是用户请求造成的异常
//不应当记录日志
$this->code = $e -> code;
$this->msg= $e -> msg;
$this->errorCode= $e -> errorCode;
} else {
// 如果是服务器未处理的异常,将http状态码设置为500,并记录日志
$this->code = 500;
$this->msg ='服务器内部错误';
$this->errorCode = 999;
}
$request = Request::instance();
$result = [
'msg' => $this->msg,
'error_code ' => $this->errorCode ,
//返回url
'request_url' => $request->url(),
];
return json($result, $this->$code);
}
}
测试自定义异常
applicationapimodelBanner.php
class Banner
{
public static function getBannerById($id)
{
return null;
}
}
applicationapicontrollerv1Banner.php
class Banner
{
public function getBanner($id)
{
$validate = new IDMustBePositiveInt();
$validate->goCheck();
$banner = BannerModel::getBannerByID($id);
if (!$banner) {
throw new BannerMissException();
}
return $banner;
}
}
日志系统
记录日志才能发现和解决问题
public/index.php
define('LOG_PATH', __DIR__. '/../log/'); //用自定义的路径覆盖tp5默认日志路径,要放在加载框架引导之前
在全局异常处理中加入记录日志
关闭tp5默认记录日志行为
applicationconfig.php
return [
'log' => [
// 日志记录方式,内置 file socket 支持扩展
// 关闭自动记录日志,请将type设置为test
'type' => 'test',
// 日志保存目录
'path' => LOG_PATH,
// 日志记录级别
'level' => ['sql'],
],
]
加入记录日志
applicationlibexceptionExceptionHandler.php
class ExceptionHandler extends Handle
{
public function render(Exception $e){
if($e instanceof BaseException) {
$this->code = $e -> code;
$this->msg= $e -> msg;
$this->errorCode= $e -> errorCode;
} 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);
}
private function recordErrorLog(Exception $e)
{
Log::init([
'type' => 'File', //用文件方式保存日志
'path' => LOG_PATH,
'level' => ['error'] //低于这个级别不会被记录
])
Log::record($e->getMessage(),'error');
}
}
全局异常处理应用
加个开关,让前后台都能接收到方便阅读的信息
applicationlibexceptionExceptionHandler.php (最终代码)
<?php
namespace applibexception;
use thinkexceptionHandle;
use thinkLog;
use thinkRequest;
use Exception;
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{
// config是tp5助手函数,用于读取配置信息
if(config('app_debug')){
// 调试状态下需要显示TP默认的异常页面,是供后端人员浏览的信息,错误不会被记录日志
return parent::render($e);
}
$this->code = 500;
$this->msg = 'sorry,we make a mistake. (^o^)Y';
$this->errorCode = 999;
$this->recordErrorLog($e);
}
$request = Request::instance();
$result = [
'msg' => $this->msg,
'error_code' => $this->errorCode,
'request_url' => $request = $request->url()
];
return json($result, $this->code);
}
/*
* 将异常写入日志
*/
private function recordErrorLog(Exception $e)
{
Log::init([
'type' => 'File',
'path' => LOG_PATH,
'level' => ['error']
]);
Log::record($e->getMessage(),'error');
}
}
参数异常错误
applicationlibexceptionParameterException.php
class ParameterException extends BaseException{
public $code = 400;
public $msg = '参数错误';
public $errorCode = 10000;
}
applicationapivalidateBaseValidate.php
class BaseValidate extends Validate
{
// 检测所有客户端发来的参数是否符合验证类规则
public function goCheck()
{
$request = Request::instance();
$params = $request->param();
$result = $this->check($params);
if (!$result) {
$e = new ParameterException()
$e->msg = $this->error;
throw $e
} else {
return true;
}
}
}
全局异常处理应用
使用构造函数
applicationapivalidateBaseValidate.php(最终代码)
<?php
namespace appapivalidate;
use applibexceptionParameterException;
use thinkRequest;
use thinkValidate;
class BaseValidate extends Validate
{
// 检测所有客户端发来的参数是否符合验证类规则
public function goCheck()
{
$request = Request::instance();
$params = $request->param();
//同时校验多个规则
$result = $this->batch()->check($params);
if (!$result) {
$e = new ParameterException([
'msg' => $this->error,
'code' => 400,
'errorCode' => 10002
]);
throw $e;
} else {
return true;
}
}
}
构造函数形式要先对BaseException做如下更改
applicationlibexceptionBaseException.php (最终代码)
<?php
namespace applibexception;
use thinkException;
class BaseException extends Exception
{
public $code = 400;
public $msg = 'invalid parameters';
public $errorCode = 999;
/**
* 构造函数,接收一个关联数组
* @param array $params 关联数组只应包含code、msg和errorCode,且不应该是空值
*/
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'];
}
}
}