_表达,从简单开始。--《Robin Williams:写给大家看的设计书》_
##1.14.1 统一返回的格式
很明显地,默认情况下,我们选择了 **JSON** 作为统一的格式返回接口结果。这里简单说明一下选取JSON统一返回的原因:
* JSON当前很流行,且普通接口都采用此格式返回
* JSON在绝大部分开发语言中都支持,跨语言
* JSON在浏览器浏览时,有可视化插件支持,如FF下:
![apic](http://webtools.qiniudn.com/20150411005257_74d77c625f39db43cab421787f9674f4)
##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
可以看到:
![apic](http://7qnay5.com1.z0.glb.clouddn.com/20150615.png)
####注释格式
格式是以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 各状态码产生的时机
![各状态码产生的时机](http://webtools.qiniudn.com/20150413210910.jpg)
##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:接口文档参考模板