在對接第三方介面的時候,總是會看到介面字尾會帶著v1,v2這樣的標識,我們知道這些都是介面版本的概念,那麼如果我方需要提供對外的介面,或者對接web端和APP端的時候,希望公用同一個介面,但是介面所渲染的資料表現形式不太一致,以及介面授權也不太一致的情況下,如何做到使用不同版本,且不同版本直接互不影響且同時共存呢?
首先筆者在考慮到介面設計時,有幾大模組:
- 控制器層(controller):筆者將其定義為入口層(相當於java的dao層)
- 服務層(services):邏輯服務層,控制器入口層透過版本號標識轉接到不同的服務層,具體的程式碼邏輯實現都在此處編寫
- 行為層(behavior):也可理解為事件層,服務中介軟體,行為鉤子都將在此處控制,入口許可權過濾校驗,對接第三方服務擴充套件透過行為鉤子抽出,不讓其加大服務層的程式碼臃腫
- 模型層(model):該層根據實際業務和開發習慣而定,可要可不要
- 校驗層(validate):筆者認為很有必要,所有獨立拉出一個目錄來做相關校驗,不管是獨立校驗,校驗引擎,還是框架自帶校驗都在該目錄定義,方便維護和擴充套件
- 公共層(common):系統公共程式碼,比如附件上傳,下載等
- 配置層(config):內部配置,根據需求自定義是否需要
- 語言包(lang):根據需求而定
- 複用層(tarits):根據實際需求而定
- 任務層(job):根據實際需求而定
目錄層級如圖所示:
那麼在入口層如何轉接到服務層呢?因為在這過程我們會將介面中的版本號轉接到不同的版本服務層。
首先在控制器入口層寫一個基類控制器,後續所有的控制器都將會繼承該類,在建構函式中調取行為類中的解析服務層程式碼,將服務層類初始化給基類變數!
public $service = null;
/**
* 建構函式處理頭部請求
*
* @return void
*/
public function __construct($type = 0, Request $request)
{
// 登入跳過
if (!$type) {
// 註冊行為監聽
Hook::add('app_init', [
// 校驗請求介面的身份(身份驗證)
'app\\saas\\behavior\\AuthToken'
]);
Hook::listen('app_init', []);
}
// 立即執行初始化控制器服務應用
$this->service = Hook::exec('app\\saas\\behavior\\InitializtionService', ['tag' => $type, 'request' => $request]);
}
服務InitializtionService解析路由,判斷,將服務層例項化
public function run($params)
{
// 相容控制器分層,最佳化控制器目錄結構
$controller = request()->controller();
$controllerArray = explode('.', $controller);
$controllerLength = count($controllerArray);
$appendControllerName = '';
if ($controllerLength == 1) {
$appendControllerName = $controllerArray[0];
} else {
for ($i = 0; $i < $controllerLength - 1; $i++) {
$appendControllerName .= strtolower($controllerArray[$i]) . '\\';
}
$appendControllerName .= ucfirst($controllerArray[($controllerLength - 1)]);
}
// $controller = '\\app\\saas\\controller\\' . request()->controller();
$controller = '\\app\\saas\\controller\\' . $appendControllerName;
$verion = request()->param('version');
$init_service = function () use ($controller, $verion, $params) {
// dump($controller);
// $controller = '\app\saas\controller\test\Test';
$reflection = new \ReflectionClass($controller);
if (property_exists($controller, 'versions')
&& isset($reflection->getStaticProperties()['versions'][$verion])
) {
// 預設規則返回,在前在後不允許返回其他資訊
$service = $reflection->getStaticProperties()['versions'][$verion];
// 判斷控制器服務檔案是否存在
return class_exists($service) ? new $service($params['tag'], $params['request']) : Merror::getInstance()->jsonApi(40006);
} else {
Merror::getInstance()->jsonApi(40001);
}
};
return is_null($verion) ? Merror::getInstance()->jsonApi(40002) : $init_service();
}
這樣在控制器中檔案定義如下呼叫服務層邏輯程式碼,而不用關心是屬於哪個服務層類,服務層程式碼只和版本有關
class Sysorder extends Saas
{
/**
* 版本服務排程屬性--必須預設一個且是v1
*
* @var array
*/
protected static $versions = [
'v1' => \app\saas\services\syscenter\Sysorder::class,
];
/**
* 獲取資訊集許可權目錄
*
* @method POST|GET
* @name getSubMenuListCate
*/
public function getSubMenuListCate()
{
return json($this->service->getSubMenuListCate());
}
/**
* 獲取列表
*
* @method POST|GET
* @name getSysOrderList
*/
public function getSysOrderList()
{
return json($this->service->getSysOrderList());
}
結語:此設計拋磚引玉,具體實現看各位phper大顯神通了!
api多版本介面設計模式,可以參考(基於ThinkPhp5.1實現,框架不同,設計理念一致):www.kancloud.cn/lijianlin/ethantp5...
最後推廣一下筆者自研的一套基於laravel設計的工作流流程引擎(https://learnku.com/laravel/t/48967),歡迎研究自研!
本作品採用《CC 協議》,轉載必須註明作者和本文連結