TP5/TP5.1框架自带的异常抛出模板内容十分丰富,不仅可以得到准确的错误信息还可以定位到错误行数、代码,以及接收到的各项数据,但是这往往是开发过程中用于排查错误的方式之一,但是开发API中我们在异常处理过程中不可能将这种Html的错误信息返回,那么就需要一种方式可以很方便的用于处理各种异常抛出,例如API请求中异常抛出返回JSON,想要调试时可以返回Html给开发者,再或者是项目正式上线后同样需要分开处理异常。
此文就基于实际项目中遇到的需求讲解一下处理方案。
首先简单说明一下近期接到项目的情况:由于开发进度比较赶所以该项目将后台CMS系统和对接APP的API全部在一个TP5框架下同时开发,首先第一个需求就是在开发过程中需要自定义异常,后台CMS开发需要抛出具体的Html错误模板,API开发时需要抛出Html错误模板用于调试,开发完成后可以抛出JSON,在CMS/API全部开发完成准备上线时只需关闭调试模式即可分别处理不同异常。
首先我们需要定义自己的自定义异常,我的项目中将此自定义异常类放入app目录下,这里lib/exception是自定义目录,可以随意命名。
首先我们需要在exception中新建一个自定义异常基类,这里起名为BaseException,基本思路也很简单,就是接收到控制器中抛出异常时想要返回的错误码错误信息等,由于我们需求中需要分模块处理,所以我这里加入了一个module字段用于判断异常来自哪个模块,下面贴出完整代码:
<?php
namespace app\lib\exception;
use think\Exception;
/**
* Class BaseException
* 自定义异常类的基类
* @author:Sol
*/
class BaseException extends Exception {
//状态码
public $code = 400;
//错误信息
public $msg = 'invalid parameters';
//所属模块名,用于判断后台还是接口,留空为后台admin
public $module = '';
/**
* 构造函数,接收一个关联数组
* @param array $params 关联数组只应包含code、msg、module,且不应该是空值
* @author:Sol
*/
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('module',$params))
{
$this->module = $params['module'];
}
}
}
这里仅仅是接受到异常信息。具体抛出逻辑需要在自定义一个Handle类,在lib目录下在新建ExceptionHandle类并继承框架中的Handle,我们只需要在抛出规则中判断异常是否来自我们定义的BaseException即可,如果来自BaseException则写我们自己的处理方法,如果不是则正常抛出框架准备的Html模板,代码如下:
<?php
namespace app\lib\exception;
use think\exception\Handle;
use Exception;
class ExceptionHandler extends Handle {
private $code;
private $msg;
public function render(Exception $exception)
{
if ($exception instanceof BaseException) {
$this->code = $exception->code;
$this->msg = $exception->msg;
//异常抛出位置来自Api/Admin分开处理
if ($exception->module == 'api') {
} else {
if (config('app_debug')) {
return parent::render($exception);
}
$this->code = 500;
$this->msg = '页面发生错误,请稍后再试';
}
$result = [
'code' => $this->code,
'msg' => $this->msg
];
return json($result);
}
else
return parent::render($exception);
}
}
相信大家也可以看懂其中逻辑,如果异常位置来自BaseException并且模块为API则抛出Json,若不是API且打开了APP-DEBUG调试模式则返回HTML模板,若关闭调试模式则返回Json错误信息或你想要的前端错误模版。
其中moudle参数的值我们可以通过request()->module();获得。
接下来演示一个实例看一下如何运用这个自定义异常类:
public $userInfoInToken = [];
public $token = '';
public $module = '';
public $getData = '';
public function _initialize()
{
$this->module = request()->module();
$this->getData = request()->param();
$this->checkToken($this->getData);
}
public function checkToken($getData)
{
$headers = request()->header();
$token = isset($headers['token']) ? $headers['token'] : '';
if (Db::table('user')->where('id', '=', $getData['id'])->value('token') == $token) {
$this->token = $token;
$this->userInfoInToken = Db::table('user')->where('id', '=', $getData['id'])->find();
return true;
} else
throw new TokenException(['module' => $this->module]);
}
这是用于验证Token的基类控制器,所有API调用前都会执行initalize初始化方法判断Token是否失效,其中TokenException需要继承我们的自定义异常基类BaseException:
<?php
namespace app\lib\exception;
class TokenException extends BaseException
{
//1000=Token错误 1001=Token信息和请求参数信息不符
public $code = 1000;
public $msg = 'Token验证失败';
}
除了这种直接抛出我们同样可以使用try catch来捕获意料外的错误
try
{}
catch(Exception $e)
{throw new BaseException(['msg'=>$e->getMessage()])}