表達(dá),從簡(jiǎn)單開始。--《Robin Williams:寫給大家看的設(shè)計(jì)書》
很明顯地,默認(rèn)情況下,我們選擇了 JSON 作為統(tǒng)一的格式返回接口結(jié)果。這里簡(jiǎn)單說(shuō)明一下選取JSON統(tǒng)一返回的原因:
JSON在瀏覽器瀏覽時(shí),有可視化插件支持,如FF下:
通常,我們正常情況下請(qǐng)求接口會(huì)返回類似:
{
"ret": 200,
"data": {
"title": "Default Api",
"content": "PHPer您好,歡迎使用PhalApi!",
"version": "1.1.0",
"time": 1423142802
},
"msg": ""
}
其中,ret表示為返回狀態(tài)碼,200表示成功;data為領(lǐng)域業(yè)務(wù)數(shù)據(jù),由接口自定義;最后msg為錯(cuò)誤的提示信息。下面分別解釋之。
參照HTTP的狀態(tài)碼,特約定:
當(dāng)返回200時(shí),需要同時(shí)返回data部分?jǐn)?shù)據(jù),以便客戶端實(shí)現(xiàn)所需要的業(yè)務(wù)功能。
此類請(qǐng)求是由客戶端不正確調(diào)用引起的,如請(qǐng)求的接口服務(wù)不存在,或者接口參數(shù)不對(duì),驗(yàn)證失敗等等。當(dāng)這種情況發(fā)生時(shí),客戶端同學(xué)只需要調(diào)整修正調(diào)用即可。
對(duì)于此系統(tǒng)的狀態(tài)碼,在進(jìn)行接口開發(fā)時(shí),可由項(xiàng)目自已定義約定。 通常地,我們需要告知客戶端簽名失敗時(shí),可以這樣:
throw new PhalApi_Exception_BadRequest('wrong sign', 1);
即拋出PhalApi_Exception_BadRequest異常即可,錯(cuò)誤信息會(huì)返回客戶端,對(duì)應(yīng)msg字段;狀態(tài)為1,系統(tǒng)對(duì)此類的異常會(huì)在400基礎(chǔ)上相加的,即: 401 = 400 + 1 。
此類錯(cuò)誤是應(yīng)該避免的,但當(dāng)客戶端發(fā)現(xiàn)有這種情況時(shí),應(yīng)該知會(huì)后臺(tái)接口開發(fā)人員進(jìn)行修正。
如當(dāng)配置的參數(shù)規(guī)則不符合要求時(shí),或者獲取了不存在的參數(shù)等即會(huì)觸發(fā)此類異常錯(cuò)誤,通常由框架拋出。
data為接口和客戶端主要溝通對(duì)接的數(shù)據(jù)部分,可以為任何類型,由接口自定義。但為了更好地?cái)U(kuò)展、向后兼容,建議都使用array。
當(dāng)我們?cè)陂_發(fā)接口時(shí),可以通過為接口添加注釋的方式來(lái)定義接口的返回格式,然后就可以為外部提供在線文檔的實(shí)時(shí)查看了。
如:
<?php
class Api_User extends PhalApi_Api {
/**
* 獲取用戶基本信息
* @desc 用于獲取單個(gè)用戶基本信息
* @return int code 操作碼,0表示成功,1表示用戶不存在
* @return object info 用戶信息對(duì)象
* @return int info.id 用戶ID
* @return string info.name 用戶名字
* @return string info.note 用戶來(lái)源
* @return string msg 提示信息
*/
public function getBaseInfo() {
// ... ...
}
然后在瀏覽器訪問:
http://demo.phalapi.net/checkApiParams.php?service=User.getBaseInfo
可以看到:
格式是以docs的 return 注釋來(lái)標(biāo)明的,其格式為:
@return 返回的類型 字段名字路徑(以點(diǎn)號(hào)連接) 字段名字及解析
其中,返回的類型可以為:
關(guān)鍵字 | 說(shuō)明 |
---|---|
string | 字符串 |
int | 整型 |
float | 浮點(diǎn)型 |
boolean | 布爾型 |
date | 日期 |
array | 數(shù)組 |
fixed | 固定值 |
enum | 枚舉類型 |
object | 對(duì)象 |
溫馨提示:array與object的區(qū)別
array是指沒有下標(biāo)的一個(gè)數(shù)組集合,或者有下標(biāo)但下標(biāo)是連續(xù)的自然數(shù),且各元素的結(jié)構(gòu)相同;object則是指一個(gè)結(jié)構(gòu)體,類似字典。
此外,為了明確數(shù)組與對(duì)象間的返回格式,我們也推薦如果是元素來(lái)自數(shù)組,則在返回字段的后面添加方括號(hào)來(lái)表明,以提醒客戶端在接收到此類返回時(shí)需要循環(huán)處理。如:
* @return array list 用戶列表
* @return int list[].id 用戶ID
* @return string list[].name 用戶名字
* @return string list[].note 用戶來(lái)源
當(dāng)需要對(duì)接口進(jìn)行更多說(shuō)明時(shí),可使用@desc注釋,即:
* @desc 用于獲取單個(gè)用戶基本信息
當(dāng)返回狀態(tài)碼不為200時(shí),此字段不為空。即當(dāng)有異常(如上面所說(shuō)的客戶端非法請(qǐng)求和服務(wù)端運(yùn)行錯(cuò)誤兩大類)觸發(fā)時(shí),會(huì)自動(dòng)將異常的錯(cuò)誤信息作為錯(cuò)誤信息msg返回。
但對(duì)于服務(wù)端的異常,出于對(duì)接口隱私的保護(hù),框架在錯(cuò)誤信息時(shí)沒有過于具體地描述;相反,對(duì)于客戶端的異常,由會(huì)進(jìn)行必要的說(shuō)明,以提醒客戶端該如何進(jìn)行調(diào)用調(diào)整。
此外,我們根據(jù)需要可以考慮是否需要進(jìn)行國(guó)際化的翻譯。如果項(xiàng)目在可預(yù)見的范圍內(nèi)需要部署到國(guó)外時(shí),提前做好翻譯的準(zhǔn)備是很有幫助的。如下,開發(fā)時(shí)可以這樣返回異常錯(cuò)誤信息:
throw new PhalApi_Exception_BadRequest(T('wrong sign'), 1);
當(dāng)使用的是HTTP/HTTPS協(xié)議時(shí),并且需要設(shè)置頭部header時(shí),可以使用PhalApi_Response::addHeaders()
進(jìn)行設(shè)置。對(duì)于JSON格式的返回默認(rèn)設(shè)置的頭部有:
Content-Type:"application/json;charset=utf-8"
若需要其他的頭部設(shè)置,可類似這樣,在init.php
初始化文件進(jìn)行設(shè)置跨域訪問(但通常不建議允許全部跨域,這里僅作演示):
DI()->response->addHeaders('Access-Control-Allow-Origin', '*');
后面相同的頭部信息會(huì)覆蓋前面的。
我們沒有對(duì)Exception類的異常進(jìn)行捕捉,封裝返回非200的形式,是因?yàn)槲覀兂鲇谝韵碌目紤]:
在部分H5頁(yè)面異步請(qǐng)求的情況下,客戶端需要我們返回JSONP格式的結(jié)果,則可以這樣在入口文件重新注冊(cè)response:
if (!empty($_GET['callback'])) {
DI()->response = new PhalApi_Response_JsonP($_GET['callback']);
}
但是在測(cè)試環(huán)境中,我們是不希望有內(nèi)容輸出的,所以我們可以測(cè)試時(shí)這樣注冊(cè)response:
DI()->response = 'PhalApi_Response_Explorer';
當(dāng)你的項(xiàng)目需要返回其他格式時(shí),如返回XML,則可以先這樣實(shí)現(xiàn)你的格式類:
class MyResponse_XML extends PhalApi_Response {
protected function formatResult($result) {
//TODO:把數(shù)組$result格式化成XML ...
}
}
隨后,也是簡(jiǎn)單重新注冊(cè)一下即可:
DI()->response = 'MyResponse_XML';
很多時(shí)候,很多業(yè)務(wù)場(chǎng)景,客戶端在完成一個(gè)接口請(qǐng)求并獲取到所需要的數(shù)據(jù)后,需要進(jìn)行不同的處理的。
顯然,這里也有一個(gè)返回狀態(tài)碼,更準(zhǔn)備來(lái)說(shuō),是業(yè)務(wù)操作狀態(tài)碼。并且,此類的狀態(tài)依接口不同而不同,很難做到統(tǒng)一。
SO?
我們建議的是,項(xiàng)目接口在業(yè)務(wù)數(shù)據(jù)data里面統(tǒng)一再定義一個(gè)狀態(tài)碼,通常為code字段,完整路徑即: data.code ,同時(shí)為0時(shí)表示操作成功,非0時(shí)為不同的失敗場(chǎng)景。如上面的登錄:
最后,客戶端在獲取到接口返回的數(shù)據(jù)后,先統(tǒng)一判斷ret是否正常請(qǐng)求并正常返回,即ret = 200;若是,則再各自判斷操作狀態(tài)碼code是否為0,如果不為0,則提示相應(yīng)的文案并進(jìn)行相應(yīng)的引導(dǎo),如果為0,則走正常流程!
在《RESTful Web APIs》一書中提及到,標(biāo)準(zhǔn)可以劃歸到4個(gè)分類,分別是:fiat標(biāo)準(zhǔn)、個(gè)人標(biāo)準(zhǔn)、公司標(biāo)準(zhǔn)以及開放標(biāo)準(zhǔn)。
顯然,我們這里推薦的是 JSON + ret-data-msg 返回格式既不是個(gè)人標(biāo)準(zhǔn),也不是公司標(biāo)準(zhǔn)(就筆者觀察的范籌而言,未發(fā)現(xiàn)某個(gè)公司定義了此格式)。而且,也不屬于開放標(biāo)準(zhǔn),因?yàn)橐策€沒達(dá)到此程度。更多的,它是fiat標(biāo)準(zhǔn)。
我們很容易發(fā)現(xiàn),身邊的應(yīng)用、系統(tǒng)以及周圍項(xiàng)目都在使用諸如此類的返回結(jié)構(gòu)格式,如一些AJAX的接口。
當(dāng)然,我們可希望可以消除語(yǔ)義上的鴻溝,以便在后臺(tái)接口開發(fā)上有一個(gè)很好地共識(shí)。
同時(shí),JSON + ret-data-msg 返回格式也是一種領(lǐng)域特定的格式,它更多是為app多端獲取業(yè)務(wù)數(shù)據(jù)而制作的規(guī)范。雖然它不是很完美,不具備自描述消息,也沒有資源鏈接的能力,但我們認(rèn)為它是一種恰到好處的格式。
在基于JSON通用格式的基礎(chǔ)上,加以 ret-data-msg 的約束,它很好地具備了統(tǒng)一性,可能門檻低,容易理解。
更多建議: