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()])}