接口规范
接口(API)通常我们都会采用 REST 方式来提供接口, 使用 JSON 来传输数据.
由前端(APP端)和后端一起协定接口规范的内容, 确定每一个接口的地址(URL), 输入(request)和输出(response), 必要的时候详细注释每一个字段的含义和数据类型.
具体接口的定义方式
- 资源接口: 系统涉及到哪些资源, 按照 RESTful 方式定义的细粒度接口
- 操作接口: 页面涉及到哪些操作, 例如修改购物车中商品的数量, 更换优惠券等等, 也可以使用 RESTful 方式来定义
- 页面接口: 页面涉及到太多接口, 如果是一个个地调用, 会需要很多次请求, 有可以影响到前端的性能和用户感知(特别是首屏的体验), 因此可能需要将这些接口的数据合并到一起, 作成一个聚合型接口提供给前端来使用
接口要点
- 接口必须返回统一的数据结构
- 调用接口业务失败的常用错误码, 例如未授权时调用需要授权的接口返回
"status": 1
- 登录接口如何处理, 特别是同时涉及到 Web 端/微信端/App 端
- 返回数据中图片 URL 是完整的还是部分的
-
http://a.res.com/path/to/img.png
这就是完整的, 前端直接使用这个 URL -
/path/to/img.png
这就是部分的, 一般省略域名部分, 前端需要自己拼接后才能使用'http://a.res.com' + '/path/to/img.png'
-
- 返回数据中页面跳转的 URL 是给完整的还是部分的
- 内部页面返回部分的, 或者只给ID, 由前端自己拼接, 例如只给出商品ID, 让前端自己拼接商品详情页的 URL
- 外部页面返回完整的, 例如广告位要跳转去谷歌
- 返回数据中日期的格式, 是使用时间戳还是格式化好的文字
- 对于需要前端再次处理的日期值(例如根据日期计算倒计时), 可以使用时间戳(简单暴力), 例如:
1458885313711
, 或者参考 Date.prototype.toJSON 提供 ISO 标准格式(例如需要考虑时区时) - 对于纯展示用的日期值, 推荐返回为格式化好的文字, 例如:
2017年1月1日
- 对于需要前端再次处理的日期值(例如根据日期计算倒计时), 可以使用时间戳(简单暴力), 例如:
- 分页参数和分页信息
- 如何限制只返回 N 条数据(limit 参数)
- 如何控制每页的数据条数(pageSize 参数)
- 如何加载某一页的数据(page 参数)
- 第一页是从 0 开始还是从 1 开始
- 避免无限滚动加载可能出现的重复数据(采用 lastId 分页方式, 来避免传统分页方式的弊端)
- 假设数据是按照新增时间倒序排列的
- 首先加载 2 页的数据
- 等了很久
- 期间新增了很多数据
- 再获取第 3 页数据
- 此时就可能出现重复数据的情况, 因为新增的数据都排在最前面, 后面会接着已经加载过数据
- 分页信息包含什么(total, page, pageSize)
- 分页信息何时表明已经是最后一页了
- 请求某页数据时返回的数据条数 < pageSize
- 请求某页数据时返回的数据条数 = 0
- 如果碰巧最后一页有 pageSize 条数据, 前端无法通过数据条数来判断已经处于最后一页了
接口文档(示例)
{
"code": 100000,
"msg": {
"message": "服务器正忙",
"detail": {
"exception": "PHP.util.List"
}
}
"data": {
"account": {
"id": 10001,
"communityId": 10002,
"userName": "示例",
"thumbPic": "http://api.yourdomain.com/upload/headPhoto/defaultPhoto.png",
"token": "FWbfj5E4v6cn1tf385908m007fqssnsunrt"
},
"forum": {
"forumId": 1,
"forumName": "测试社区"
},
"build": {
"buildId": 1,
"buildName": "测未来"
}
}
}
请求接口
接口 Root Endpoint 推荐为: http://api.yourdomain.com
.
向接口传递参数时, 推荐在 HTTP 请求体(body
)中包含一个 JSON Object 作为接口的参数, 并设置 Content-Type: application/json; charset=utf-8
. 如有少量参数可以补充到 URL query string 或者作为 Content-Type: application/x-www-form-urlencoded
放在请求体(body
)中
例如
查询 VIP 用户的接口
POST /users?limit=10 HTTP/1.1
Content-Type: application/json; charset=utf-8
{
"name": "hanmeimei",
"isVip": true
}
接口返回
返回的响应体类型推荐为 Content-Type: application/json; charset=utf-8
, 返回的数据包含在 HTTP 响应体中, 是一个 JSON Object. 该 Object 可能包含 3 个字段 data
, status
, statusInfo
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"Code": 100000,
"msg": {
"message": "操作成功",
}
"Data": {}
}
字段名 | 字段说明 |
---|---|
code |
状态码 必须是 >= 0 的 JSON Number 整数.0 表示请求处理成功, 此时可以省略 Code 字段, 省略时和为 0 时表示同一含义.非 0 表示发生错误时的错误码错误码格式可以参考微博API的 Error code"), 此时可以省略 data 字段, 并视情况输出 Msg 字段作为补充信息 |
msg |
状态信息 必须是任意 JSON 数据类型. 推荐始终返回一个 object 包含 message 和 detail 字段message 字段作为接口处理失败时, 给予用户的友好的提示信息, 即所有给用户的提示信息都统一由后端来处理.detail 字段用来放置接口处理失败时的详细错误信息. 只是为了方便排查错误, 前端无需使用. |
data |
业务数据 必须是任意 JSON 数据类型(number/string/boolean/object/array). 推荐始终返回一个 object (即再包一层)以便于扩展字段. 例如: 用户数据应该返回 {"user":{"name":"test"}} , 而不是直接为 {"name":"test"}
|
例如
-
接口处理成功时接口返回的数据
{ "data": "api result" "code": 0 }
-
接口处理失败时接口返回的数据
{ "code": 1, "msg": { "message": "服务器正忙", "detail": { "exception": "java.util.List" } } }
这样我们就可以非常容易地通过判断 status 来处理数据了
if (!response.Code) {
// status 为 0 或者没有 status 字段时表示接口成功返回了数据
console.log(response.data);
} else {
// 失败
console.error(response.code, response.msg);
// 统一由服务端返回给用户的提示信息
alert(response.msg.message);
}