快速入門(mén): 接口開(kāi)發(fā)示例(源碼+圖文)

2018-11-21 10:23 更新

一個(gè)真正的強(qiáng)者,不是擺平了多少人,而是他能幫助到多少人。 --開(kāi)源中國(guó)源創(chuàng)會(huì)分享廣州站 @海洋之心-悟空


1.11.1 模擬開(kāi)發(fā):獲取開(kāi)源中國(guó)用戶信息接口

首次使用此接口開(kāi)發(fā)框架時(shí),可以先查看此開(kāi)發(fā)示例。

假設(shè),我們需要為開(kāi)源中國(guó)打造一個(gè)平放平臺(tái),其中有一個(gè)接口是可以根據(jù)用戶ID來(lái)獲取用戶的基本信息。

本文,就以模擬獲取開(kāi)源中國(guó)用戶信息接口開(kāi)發(fā)(即:從數(shù)據(jù)庫(kù)獲取用戶的基本信息并以JSON格式返回給客戶端)為例,簡(jiǎn)明的接口開(kāi)發(fā)中的流程,以及用到的基本功能和操作,主要包括有:統(tǒng)一入口文件、參數(shù)規(guī)則配置、接口層/領(lǐng)域?qū)?模型持久層、日志紀(jì)錄、數(shù)據(jù)庫(kù)操作、配置讀取、國(guó)際化翻譯等。

以給大家一個(gè)感觀的認(rèn)識(shí)。

最終接口的調(diào)用與返回結(jié)果如下:

//接口請(qǐng)求格式
http://dev.phalapi.com/demo/?service=User.GetBaseInfo&user_id=帳號(hào)ID

//返回結(jié)果格式
{
    "ret": 200,
    "data": {           
        "code": 0,    //狀態(tài)碼,0表示正常獲取,1表示用戶不存在
        "msg": "",
        "info": {      //用戶信息
            "id": "1",    //用戶ID
            "name": "dogstar",   //帳號(hào)
            "note": "oschina"   //來(lái)源
        }
    },
    "msg": ""
}

1.11.2 開(kāi)發(fā)流程

(1) 統(tǒng)一入口文件

為了更好保護(hù)我們的項(xiàng)目代碼,建議將接口的訪問(wèn)路徑設(shè)置在:./PhalApi/Pubic目錄,并且各套接口(如按版本分:v1/v2/v3等等;按不同終端分:ios/android/pc等)各自獨(dú)立入口。所以本示例中將./PhalApi/Pubic/demo/index.php下。

參數(shù)入口文件的寫(xiě)法,我們可以快速得到基本的入口文件如下:

// $ vim ./Public/demo/index.php 

<?php
/**
 * Demo 統(tǒng)一入口
 */

require_once dirname(__FILE__) . '/../init.php';

//裝載你的接口
DI()->loader->addDirs('Demo');

/** ---------------- 響應(yīng)接口請(qǐng)求 ---------------- **/

$server = new PhalApi();
$rs = $server->response();
$rs->output();

此外,我們還需要一個(gè)公共的初始化文件:

//$ vim ./Public/init.php 

<?php
/**
 * 統(tǒng)一初始化
 */

/** ---------------- 根目錄定義,自動(dòng)加載 ---------------- **/

date_default_timezone_set('Asia/Shanghai');

defined('API_ROOT') || define('API_ROOT', dirname(__FILE__) . '/..');

require_once API_ROOT . '/PhalApi/PhalApi.php';
$loader = new PhalApi_Loader(API_ROOT);

/** ---------------- 注冊(cè)&初始化服務(wù)組件 ---------------- **/

//自動(dòng)加載
DI()->loader = $loader;

//配置
DI()->config = new PhalApi_Config_File(API_ROOT . '/Config');

//日志紀(jì)錄
DI()->logger = new PhalApi_Logger_File(API_ROOT . '/Runtime', 
    PhalApi_Logger::LOG_LEVEL_DEBUG | PhalApi_Logger::LOG_LEVEL_INFO | PhalApi_Logger::LOG_LEVEL_ERROR);

//數(shù)據(jù)操作 - 基于NotORM,$_GET['__sql__']可自行改名
DI()->notorm = function() {
    $debug = !empty($_GET['__sql__']) ? true : false;
    return new PhalApi_DB_NotORM(DI()->config->get('dbs'), $debug);
};

//調(diào)試模式,$_GET['__debug__']可自行改名
DI()->debug = !empty($_GET['__debug__']) ? true : DI()->config->get('sys.debug');

//翻譯語(yǔ)言包設(shè)定
SL('zh_cn');

(2)TDD測(cè)試驅(qū)動(dòng)開(kāi)發(fā)

遵循最佳實(shí)踐,我們?cè)诰帉?xiě)代碼前先編寫(xiě)單元測(cè)試。但同時(shí)為了減少編寫(xiě)測(cè)試代碼的痛苦,我們可以先定義接口函數(shù)簽名,再通過(guò)腳本自動(dòng)生成測(cè)試代碼骨架來(lái)提高我們的開(kāi)發(fā)效率。

//$vim ./Demo/Api/User.php

<?php

class Api_User extends PhalApi_Api
{   
    public function getBaseInfo()
    {
    }
}

通過(guò)腳本生成測(cè)試骨架:

$ mkdir -p ./Demo/Tests/Api/
$ cd ./Demo/Tests/Api
$ php ../../../PhalApi/build_phpunit_test_tpl.php ../../Api/User.php Api_User ./../test_env.php

然后,根據(jù)/Public/demo/index.php入口文件,也搭建一個(gè)測(cè)試環(huán)境的入口文件:

vim ./Demo/Tests/test_env.php

修正一下Api_Examples_User_Test.php里,測(cè)試環(huán)境test_env.php的包含路徑:

//手動(dòng)調(diào)用test_env.php的路徑
require_once dirname(__FILE__) . '/../test_env.php';

并且,修改測(cè)試,以符合我們通過(guò)userId=1獲取基本信息(名字為dogstar,來(lái)源為oschina):

//$vim ./Demo/Tests/Api/Api_User_Test.php

    /**
     * @group testGetBaseInfo
     */
    public function testGetBaseInfo()
    {
        $str = 'service=User.GetBaseInfo&user_id=1';
        parse_str($str, $params);

        DI()->request = new PhalApi_Request($params);

        $api = new Api_User(); 
        //自己進(jìn)行初始化
        $api->init();
        $rs = $api->getBaseInfo();

        $this->assertNotEmpty($rs);
        $this->assertArrayHasKey('code', $rs);
        $this->assertArrayHasKey('msg', $rs);
        $this->assertArrayHasKey('info', $rs);

        $this->assertEquals(0, $rs['code']);

        $this->assertEquals('dogstar', $rs['info']['name']);
        $this->assertEquals('oschina', $rs['info']['note']);
    }

此時(shí),單元測(cè)試是預(yù)期失敗的:

$ phpunit ./Api_User_Test.php 
PHPUnit 4.3.4 by Sebastian Bergmann.

F

Time: 3 ms, Memory: 5.25Mb

There was 1 failure:

1) PhpUnderControl_ApiUser_Test::testGetBaseInfo
Failed asserting that a NULL is not empty.

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

(3)Api接口層

此接口層,主要是負(fù)責(zé)響應(yīng)客戶端的請(qǐng)求,調(diào)用需要的領(lǐng)域?qū)舆M(jìn)行必要的服務(wù)功能提供。

配置參數(shù)規(guī)則

為了獲取到用戶ID,我們可以在getRules()函數(shù)里面定義參數(shù)規(guī)則,以便框架自動(dòng)幫我們過(guò)濾獲取需要的參數(shù)。

溫馨提示:
接口層的全部類成員函數(shù)都應(yīng)以小寫(xiě)開(kāi)頭。
但對(duì)外,函數(shù)首字母不區(qū)分大小寫(xiě),因?yàn)榭蚣軙?huì)將請(qǐng)求的函數(shù)強(qiáng)制轉(zhuǎn)換成小寫(xiě)再執(zhí)行。

原因在于:
1、我們堅(jiān)持駝峰法的代碼風(fēng)格;
2、對(duì)外界我們可以統(tǒng)一使用大寫(xiě)來(lái)提供服務(wù)名稱,如:User.Login,這樣更顯專業(yè)。

//$vim ./Demo/Api/User.php

<?php

class Api_User extends PhalApi_Api
{
    public function getRules()
    {
        return array(
            'getBaseInfo' => array(
                'userId' => array('name' => 'userId', 'type' => 'int', 'min' => 1, 'require' => true),
            ),
        );
    }

    //...

如上,我們就定義了getBaseInfo接口中的userId參數(shù),名字也為userId,整型,最小值為1,必須。

接口實(shí)現(xiàn)

//$vim ./Demo/Api/User.php

    public function getBaseInfo()
    {
        $rs = array('code' => 0, 'msg' => '', 'info' => array());

        $domain = new Domain_User();
        $info = $domain->getBaseInfo($this->userId);

        if (empty($info)) {
            DI()->logger->debug('user not found', $this->userId);

            $rs['code'] = 1;
            $rs['msg'] = T('user not exists');
            return $rs;
        }

        $rs['info'] = $info;

        return $rs;
    }

(4)Domain領(lǐng)域?qū)?/h3>

領(lǐng)域?qū)又饕顷P(guān)注復(fù)雜業(yè)務(wù)的處理,以及緩存的處理、耗時(shí)操作后臺(tái)異步處理等,并調(diào)用Model持久層獲取需要的數(shù)據(jù)。因此,是Api與Model層之間的橋梁。

在此示例中,我們只需要簡(jiǎn)單地調(diào)用Model層獲取用戶的信息即可,再加強(qiáng)一下用戶ID的合法性判斷。

//$ vim ./Demo/Domain/User.php 

<?php

class Domain_User
{
    public function getBaseInfo($userId)
    {
        $rs = array();

        $userId = intval($userId);
        if ($userId <= 0) {
            return $rs;
        }

        $model = new Model_User();
        $rs = $model->getByUserId($userId);

        return $rs;
    }
}

(5)Model持久層

此一層主要關(guān)注數(shù)據(jù)從持久存儲(chǔ)的獲取,特別是針對(duì)數(shù)據(jù)庫(kù)的操作,但不排除其他媒介,如文件、緩存等。

首先,先準(zhǔn)備一下我們需要的表和數(shù)據(jù):

CREATE TABLE `phalapi_test`.`tbl_user` (
  `id` INT NOT NULL,
  `name` VARCHAR(45) NULL,
  `note` VARCHAR(45) NULL,
  PRIMARY KEY (`id`));

INSERT INTO `phalapi_test`.`tbl_user` (`id`, `name`, `note`) VALUES ('1', 'dogstar', 'oschina');

然后,編寫(xiě)需要的Model代碼,即利用NotORm實(shí)現(xiàn)對(duì)數(shù)據(jù)的操作:

//$ vim ./Demo/Model/User.php 

<?php

class Model_User
{
    public function getByUserId($userId)
    {
        return DI()->notorm->user->select('*')->where('id = ?', $userId)->fetch();
    }
}

(6)單元測(cè)試通過(guò)啦!

在完成上面簡(jiǎn)單的開(kāi)發(fā)后,我們即可以實(shí)現(xiàn)接口的開(kāi)發(fā),運(yùn)行一下剛才的單元測(cè)試,完美通過(guò)!

$ phpunit ./Api_User_Test.php 
PHPUnit 4.3.4 by Sebastian Bergmann.

SELECT * FROM tbl_user WHERE (id = ?); -- 1

.SELECT * FROM tbl_user WHERE (id = ?); -- 1<br />

Time: 34 ms, Memory: 6.50Mb

OK (2 tests, 7 assertions)

在單元測(cè)試的保證下,我們便可以放心大膽地將我們的代碼發(fā)布到外網(wǎng),提供給更多的開(kāi)發(fā)者,和終端用戶使用。

(7)數(shù)據(jù)庫(kù)配置

因?yàn)槭菃卧獪y(cè)試,所以我們配置搭建了新的一個(gè)測(cè)試環(huán)境,特別對(duì)于數(shù)據(jù)庫(kù)的配置,如下:

//$ vim ./Config/dbs.php

<?php
/**
 * examples配置
 */

return array(
    /**
     * DB數(shù)據(jù)庫(kù)服務(wù)器集群
     */
    'servers' => array(
        'db_demo' => array(
            'host'      => '192.168.0.104',         //數(shù)據(jù)庫(kù)域名
            'name'      => 'phalapi_test',          //數(shù)據(jù)庫(kù)名字
            'user'      => 'root',                  //數(shù)據(jù)庫(kù)用戶名
            'password'  => '123456',                //數(shù)據(jù)庫(kù)密碼
            'port'      => '3306',                  //數(shù)據(jù)庫(kù)端口
        ),
    ),

    /**
     * 自定義路由表
     */
    'tables' => array(
        '__default__' => array(
            'prefix' => 'tbl_',
            'key' => 'id',
            'map' => array(
                array('db' => 'db_demo'),
            ),
        ),
    ),
);

溫馨提示:
為了方便在單元測(cè)試時(shí)進(jìn)行調(diào)試,和查看日志,對(duì)于全部查詢、執(zhí)行的SQL語(yǔ)句都會(huì)顯示出來(lái),全部的日志改用控制臺(tái)輸出。

(8)最終接口調(diào)用

接口調(diào)用的鏈接,這時(shí)已經(jīng)相當(dāng)明了了,即:域名 + 路徑(/demo) + 參數(shù)(可從單元測(cè)試那直接獲?。?/p>

如本示例的是:

http://dev.phalapi.com/demo/?service=User.GetBaseInfo&user_id=1

返回的結(jié)果是:

{
    "ret": 200,
    "data": {
        "code": 0,
        "msg": "",
        "info": {
            "id": "1",
            "name": "dogstar",
            "note": "oschina"
        }
    },
    "msg": ""
}

截圖效果:
show

溫馨提示:
如果提示日志寫(xiě)入失敗,請(qǐng)確保./Runtime目錄具有寫(xiě)入權(quán)限,即0777。

1.11.3 更多簡(jiǎn)明的使用

(1)日志紀(jì)錄

當(dāng)我們?cè)L問(wèn)一個(gè)不存在的用戶時(shí),將會(huì)觸發(fā)日志紀(jì)錄:

DI()->logger->debug('user not found', $this->userId);

如訪問(wèn):

http://dev.phalapi.com/demo/?service=User.GetBaseInfo&user_id=2

然后,可以在Runtime下看到按天分目錄的日志:

$ tailf ./Runtime/log/201501/20150128.log 
2015-01-28 00:37:34|DEBUG|user not found|2

溫馨提示:
外網(wǎng)環(huán)境上,請(qǐng)把Runtime目錄軟鏈到磁盤(pán)空間很大的路徑。

(2)國(guó)際化翻譯

當(dāng)需要翻譯時(shí),可以使用人性化的函數(shù)T(),如:

$rs['msg'] = T('user not exists');

對(duì)應(yīng)地需要補(bǔ)充翻譯的內(nèi)容:

//$ vim ./Language/zh_cn/common.php 

    'user not exists' => '用戶不存在',

還是以上面的用戶不存在為例,看下運(yùn)行的截圖效果:show

(3)配置讀取

配置的讀取,使用方便,直接通過(guò)以下方式便可以獲取,以點(diǎn)號(hào)分割:

DI()->config->get('dbs')

第一段,必須為文件名,后面的為用點(diǎn)號(hào)相連的數(shù)組下標(biāo),不限級(jí)。

(4)[酷!]接口參數(shù)在線查詢

為了方便客戶端實(shí)時(shí)查看最新的接口參數(shù),這里提供了一個(gè)快速的在線工具:

http://demo.phalapi.net/checkApiParams.php?service=User.GetBaseInfo

用瀏覽器打開(kāi)后,即可以看到最新的接口參數(shù)說(shuō)明, 不需要后臺(tái)接口開(kāi)發(fā)人員編寫(xiě)文檔維護(hù),直接從代碼中生成參數(shù)報(bào)表:

show

1.11.4 Demo相關(guān)代碼文件

從上面可以得到,此示例相關(guān)的代碼如下:

Demo$ tree
.
├── Api
│   ├── Default.php
│   └── User.php
├── Domain
│   └── User.php
├── Model
│   └── User.php
└── Tests
    ├── Api
    │   ├── Api_Default_Test.php
    │   └── Api_User_Test.php
    └── test_env.php

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)