_表達,從簡單開始。--《Robin Williams:寫給大家看的設計書》_
##1.14.1 統一返回的格式
很明顯地,默認情況下,我們選擇了 **JSON** 作為統一的格式返回接口結果。這里簡單說明一下選取JSON統一返回的原因:
* JSON當前很流行,且普通接口都采用此格式返回
* JSON在絕大部分開發語言中都支持,跨語言
* JSON在瀏覽器瀏覽時,有可視化插件支持,如FF下:

##1.14.2 統一返回結構
通常,我們正常情況下請求接口會返回類似:
```javascript
{
"ret": 200,
"data": {
"title": "Default Api",
"content": "PHPer您好,歡迎使用PhalApi!",
"version": "1.1.0",
"time": 1423142802
},
"msg": ""
}
```
其中,ret表示為返回狀態碼,200表示成功;data為領域業務數據,由接口自定義;最后msg為錯誤的提示信息。下面分別解釋之。
###(1)返回狀態碼 ret
參照HTTP的狀態碼,特約定:
* 200:接口正常請求并返回
* 4XX:客戶端非法請求
* 5XX:服務器運行錯誤
####200 正常返回
當返回200時,需要同時返回data部分數據,以便客戶端實現所需要的業務功能。
#### 4XX 客戶端非法請求
此類請求是由客戶端不正確調用引起的,如請求的接口服務不存在,或者接口參數不對,驗證失敗等等。當這種情況發生時,客戶端同學只需要調整修正調用即可。
對于此系統的狀態碼,在進行接口開發時,可由項目自已定義約定。 通常地,我們需要告知客戶端簽名失敗時,可以這樣:
```javascript
throw new PhalApi_Exception_BadRequest('wrong sign', 1);
```
即拋出PhalApi_Exception_BadRequest異常即可,錯誤信息會返回客戶端,對應msg字段;狀態為1,系統對此類的異常會在400基礎上相加的,即: **401 = 400 + 1** 。
#### 5XX 服務器運行錯誤
此類錯誤是應該避免的,但當客戶端發現有這種情況時,應該知會后臺接口開發人員進行修正。
如當配置的參數規則不符合要求時,或者獲取了不存在的參數等即會觸發此類異常錯誤,通常由框架拋出。
###(2)業務數據 data
data為接口和客戶端主要溝通對接的數據部分,可以為任何類型,由接口自定義。但為了更好地擴展、向后兼容,建議都使用array。
####返回格式的定義與在線查看
當我們在開發接口時,可以通過為接口添加注釋的方式來定義接口的返回格式,然后就可以為外部提供在線文檔的實時查看了。
如:
```javascript
<?php
class Api_User extends PhalApi_Api {
/**
* 獲取用戶基本信息
* @desc 用于獲取單個用戶基本信息
* @return int code 操作碼,0表示成功,1表示用戶不存在
* @return object info 用戶信息對象
* @return int info.id 用戶ID
* @return string info.name 用戶名字
* @return string info.note 用戶來源
* @return string msg 提示信息
*/
public function getBaseInfo() {
// ... ...
}
```
然后在瀏覽器訪問:
http://demo.phalapi.net/checkApiParams.php?service=User.getBaseInfo
可以看到:

####注釋格式
格式是以docs的 return 注釋來標明的,其格式為:
```
@return 返回的類型 字段名字路徑(以點號連接) 字段名字及解析
```
其中,返回的類型可以為:
關鍵字|說明
---|---
string|字符串
int|整型
float|浮點型
boolean|布爾型
date|日期
array|數組
fixed|固定值
enum|枚舉類型
object|對象
> 溫馨提示:array與object的區別
> array是指沒有下標的一個數組集合,或者有下標但下標是連續的自然數,且各元素的結構相同;object則是指一個結構體,類似字典。
此外,為了明確數組與對象間的返回格式,我們也推薦如果是元素來自數組,則在返回字段的后面添加方括號來表明,以提醒客戶端在接收到此類返回時需要循環處理。如:
```javascript
* @return array list 用戶列表
* @return int list[].id 用戶ID
* @return string list[].name 用戶名字
* @return string list[].note 用戶來源
```
當需要對接口進行更多說明時,可使用@desc注釋,即:
```
* @desc 用于獲取單個用戶基本信息
```
###(3)錯誤信息 msg
當返回狀態碼不為200時,此字段不為空。即當有異常(如上面所說的客戶端非法請求和服務端運行錯誤兩大類)觸發時,會自動將異常的錯誤信息作為錯誤信息msg返回。
但對于服務端的異常,出于對接口隱私的保護,框架在錯誤信息時沒有過于具體地描述;相反,對于客戶端的異常,由會進行必要的說明,以提醒客戶端該如何進行調用調整。
此外,我們根據需要可以考慮是否需要進行國際化的翻譯。如果項目在可預見的范圍內需要部署到國外時,提前做好翻譯的準備是很有幫助的。如下,開發時可以這樣返回異常錯誤信息:
```javascript
throw new PhalApi_Exception_BadRequest(T('wrong sign'), 1);
```
##1.14.3 關于Exception類異常沒捕捉的原因
我們沒有對Exception類的異常進行捕捉,封裝返回非200的形式,是因為我們出于以下的考慮:
* 一來為了方便開發過程中快速發現及定位具體出錯的位置;
* 二來為了便于線上環境中nginx服務器對錯誤的捕捉和紀錄;
##1.14.4 JsonP格式和其他的返回
在部分H5頁面異步請求的情況下,客戶端需要我們返回JSONP格式的結果,則可以這樣在入口文件重新注冊response:
```javascript
if (!empty($_GET['callback'])) {
DI()->response = new PhalApi_Response_JsonP($_GET['callback']);
}
```
但是在測試環境中,我們是不希望有內容輸出的,所以我們可以測試時這樣注冊response:
```javascript
DI()->response = 'PhalApi_Response_Explorer';
```
##1.14.5 擴展你的返回格式
當你的項目需要返回其他格式時,如返回XML,則可以先這樣實現你的格式類:
```javascript
class MyResponse_XML extends PhalApi_Response {
protected function formatResult($result) {
//TODO:把數組$result格式化成XML ...
}
}
```
隨后,也是簡單重新注冊一下即可:
```javascript
DI()->response = 'MyResponse_XML';
```
##1.14.6 各狀態碼產生的時機

##1.14.7 更好地建議
很多時候,很多業務場景,客戶端在完成一個接口請求并獲取到所需要的數據后,需要進行不同的處理的。
* 就登錄來說,當登錄失敗時,可能需要知道:
* 是否用戶名不存在?
* 是否密碼錯誤?
* 是否已被系統屏蔽?
* 是否密碼錯誤次數超過了最大的重試次數?
* ...
顯然,這里也有一個返回狀態碼,更準備來說,是業務操作狀態碼。并且,此類的狀態依接口不同而不同,很難做到統一。
SO?
我們建議的是,項目接口在業務數據data里面統一再定義一個狀態碼,通常為code字段,完整路徑即: **data.code** ,同時為0時表示操作成功,非0時為不同的失敗場景。如上面的登錄:
* code = 0 登錄成功
* code = 1 用戶名不存在
* code = 2 密碼錯誤
* code = 3 系統已屏蔽此賬號
* code = 4 密碼錯誤次數超過了最大的重試次數
* ...
最后,客戶端在獲取到接口返回的數據后,先統一判斷ret是否正常請求并正常返回,即ret = 200;若是,則再各自判斷操作狀態碼code是否為0,如果不為0,則提示相應的文案并進行相應的引導,如果為0,則走正常流程!
##1.14.8 領域特定設計與Fiat標準
在《RESTful Web APIs》一書中提及到,標準可以劃歸到4個分類,分別是:fiat標準、個人標準、公司標準以及開放標準。
顯然,我們這里推薦的是 **JSON + ret-data-msg** 返回格式既不是個人標準,也不是公司標準(就筆者觀察的范籌而言,未發現某個公司定義了此格式)。而且,也不屬于開放標準,因為也還沒達到此程度。更多的,它是fiat標準。
我們很容易發現,身邊的應用、系統以及周圍項目都在使用諸如此類的返回結構格式,如一些AJAX的接口。
當然,我們可希望可以消除語義上的鴻溝,以便在后臺接口開發上有一個很好地共識。
同時,**JSON + ret-data-msg** 返回格式也是一種領域特定的格式,它更多是為app多端獲取業務數據而制作的規范。雖然它不是很完美,不具備自描述消息,也沒有資源鏈接的能力,但我們認為它是一種恰到好處的格式。
在基于JSON通用格式的基礎上,加以 **ret-data-msg** 的約束,它很好地具備了統一性,可能門檻低,容易理解。
- 歡迎使用PhalApi!
- 接口,從簡單開始!
- [1.1]-下載與安裝
- [1.2]-創建一個自己的項目
- [1.3]-在線體驗
- [1.4]-文檔、幫助和官網
- [1.10]-對PhalApi框架的抉擇
- [1.11]-快速入門(backup)
- [1.12]-參數規則:接口參數規則配置
- [1.13]-統一的接口請求方式:_sevice=XXX.XXX
- [1.14]-統一的返回格式和結構:ret-data-msg
- [1.15]-數據庫操作:基于NotORM的使用及優化
- [1.16]-配置讀取:內外網環境配置的完美切換
- [1.17]-日記紀錄:簡化版的日記接口
- [1.18]-快速函數:人性化的關懷
- [1.19]-DI服務速查:各資源服務一覽表
- [1.20]-DB操作:數據庫基本操作速查
- [1.21]-類的自動加載:遵循PEAR包的命名規范
- [1.22]-簽名驗證:自定義簽名規則
- [1.23]-請求和響應:GET和POST兩者皆可得及超越JSON格式返回
- [1.24]-緩存策略:更靈活地可配置化的多級緩存
- [1.25]-國際化翻譯:為走向國際化提前做好翻譯準備
- [1.26]-數據安全:數據對稱加密方案
- [1.27]-精益開發:更富表現力的Model層和重量級數據獲取的應對方案
- [1.28]-COOKIE:對COOKIE原生態的支持及記憶加密升級版
- [1.29]-開放與封閉:多入口和統一初始化
- [1.30]-保持的力量:接口開發最佳實踐
- [1.31]-新型計劃任務:以接口形式實現的計劃任務
- [2.11]-核心思想:DI依賴注入-讓資源更可控
- [2.12]-海量數據:可配置的分庫分表
- [2.13]-接口調試:在線SQL語句查看與性能優化
- [2.14]-測試驅動開發:意圖導向編程下的接口開發
- [2.15]-演進:新型計劃任務續篇
- [2.16]-領域驅動設計:應對復雜領域業務的Domain層
- [2.17]-微服務:Api接口服務層
- [2.18]-定制化:資源服務的再實現
- [2.19]-擴展庫:可重用的擴展類庫
- [2.20]-約定編程:架構明顯的編程風格
- [2.21]-服務器統一部署方案簡明版:CentOs---Nginx---php-fpm---MySql-[--Memcached]
- [2.22]-更多工具:精益項目和團隊建設
- [3.1]-擴展類庫:微信開發
- [3.2]-擴展類庫:代理模式下phprpc協議的輕松支持
- [3.3]-擴展類庫:基于PHPMailer的郵件發送
- [3.4]-擴展類庫:優酷開放平臺接口調用
- [3.5]-擴展類庫:七牛云存儲接口調用
- [3.6]-擴展類庫:新型計劃任務
- [3.8]-擴展類庫:用戶、會話和第三方登錄集成
- [3.9]-擴展類庫:swoole支持下的長鏈接和異步任務實現
- [3.11]-擴展類庫:基于FastRoute的快速路由
- [4.2]-開發實戰2:模擬優酷開放平臺接口項目開發
- [4.3]-開發實戰3:一個簡單的小型項目開發(奔跑吧兄弟投票活動)
- [5.1]-架構與思想:PhalApi核心設計和思想解讀
- [5.2]-雜談:扯一些PhalApi的前世和今生
- [5.3]-框架總結:術語表和PHP開發建議
- [5.4]-許可
- [5.5]-聯系和加入我們
- [5.6]-更新日記
- [5.8]-致框架貢獻者:加入PhalApi開源指南
- [6.1]-基于接口查詢語言的SDK包
- [6.2]-SDK包(JAVA版)
- [6.3]-SDK包(PHP版)
- [6.4]-SDK包(Objective-C版)
- [6.5]-SDK包(javascript版)
- [6.6]-SDK包(Ruby版)
- [8.1]-PhalApi視頻教程
- 附錄1:接口文檔參考模板