restful最佳实践——接口规范

为了前后端分工明确,对接流畅,确保可读性和扩展性以及高可用、一致性,特约定下述无状态RESTful API规范:

写在前面

前后端分离意味着,前后端之间使⽤ JSON 来交流,两个开发团队之间使⽤ API 作为契约进⾏交互。从此,后台选⽤的技术栈不影响前台。当我们决定需要前后端分离时,我们仍然还需要⾯对⼀系列的问题:

  1. 是否⾜够的安全?我们怎么去存储⽤户数据,使⽤ LocalStorage 的话,还要考虑加密。采⽤哪种认证⽅式来让⽤户登录,并保存相应的状态?
  2. 是否有⾜够的技术来⽀撑前后端分离?有没有能⼒创建出符合 RESTful 风格的API?
  3. 是否有能⼒维护 API 接口?当前端或者后台需要修改接⼜时,是否能轻松地修改?前端和后台两个团队是不是很容易合作?是不是可以轻松地进⾏联调?
  4. 前后端职责是否能明确?即:后台提供数据,前端负责显⽰。
  5. 是否建⽴了前端的错误追踪机制?能否帮助我们快速地定位出问题。

前后端分离的核⼼:后台提供数据,前端负责显⽰

前提

RESTful API 统一约束客户端和服务器之间的接口。简化和分离系统架构,使每个模块独立!

请求中使用URI定位资源

用HTTP Verbs[动词](GET、POST、PUT、DELETE)描述操作(具体表现形式)

数据传递(默认)采用:Content-Type: application/json; charset=utf-8

Rest

REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。**REST是设计风格而不是标准。**REST通常基于使用HTTP,URI,和XML(标准通用标记语言下的一个子集)以及HTML(标准通用标记语言下的一个应用)

统一接口(Uniform Interface)

统一接口约束定义客户端和服务器之间的接口。它简化了分离的结构,使各部分独立发展。

无状态(Stateless)

REST要求状态要么被放入资源状态中,要么保存在客户端上。或者换句话说,服务器端不能保持除了单次请求之外的,任何与其通信的客户端的通信状态。从客户端的每个请求要包含服务器所需要的所有信息。这样做的最直接的理由就是可伸缩性—— 如果服务器需要保持客户端状态,那么大量的客户端交互会严重影响服务器的内存可用空间(footprint)。

缓存(Cachable)

服务器返回信息必须被标记是否可以缓存,如果缓存,客户端可能会重用之前的信息发送请求。

客户-服务器(Client-Server)

客户端无需关注数据存储,服务器端无需关注用户界面,提高了前后端可移植性。

分层系统(Layered System)

客户端不关心直接连接到最终服务器还是连接到中间服务器。中间服务器可以通过启用负载平衡和提供共享缓存来提高系统可扩展性。分层系统也可以执行安全策略。

支持按需代码(Code on Demand,可选)

服务器可以通过传输逻辑来临时扩展或定制客户端的功能。

URL规范

GET https//domain.com/api/{模块名}/{?菜单名}/{接口名}/:param

  1. 不能使用大写,用中横线 – 不用下划线 _ ;
  2. 使用名词表示资源集合,使用复数形式(为确保所有API URIs保持一致),不能使用动词;
  3. 每个资源都至少有一个标识它的URI,同时应该遵循一个可预测的层次结构来提高可理解性,从而提高可用性;
  4. 无需在URI中增加版本号,通过HTTP请求头信息的字段中进行区分(或者在URI包含主版本信息,同时请求头包含子版本信息。

Accept: vnd.example-com.foo+json;version=1.1Accept: vnd.example-com.foo+json; version=2.0

restful最佳实践——接口规范

Request

restful最佳实践——接口规范

说明:

  1. 安全性 :不会改变资源状态,可以理解为只读的;
  2. 幂等性 :执行1次和执行N次,对资源状态改变的效果是等价的。
  3. 查询字段内容过多,统一使用POST方式查询,请求地址增加/query加以区分
  4. 批量删除,统一使用POST方式,请求地址增加/delete加以区分

由于存在批量删除的情况,而一些网关、代理、防火墙在收到DELETE请求后,会把请求的body直接剥离掉。建议将存在批量删除的接口统一改成POST提交,为了标识是删除操作,在请求路径上增加/delete。

GET

被用于获取资源。不允许对服务器上资源做任何修改操作。

示例:

http://www.example.com/customers/12345
http://www.example.com/customers/12345/orders
http://www.example.com/buckets/sample

PUT

常用于更新资源。通过请求体携带资源发送给服务器。注意:在资源ID由客户端而不是由服务器选择的情况下,也可以使用PUT来创建资源。修改成功返回200,创建成功返回201。建议使用post进行创建新资源。

http://www.example.com/customers/12345
http://www.example.com/customers/12345/orders/98765
http://www.example.com/buckets/secret_stuff

POST

常用于创建新资源。创建成功通常返回201。

http://www.example.com/customers
http://www.example.com/customers/12345/orders

DELETE

删除资源。

http://www.example.com/customers/12345
http://www.example.com/customers/12345/orders
http://www.example.com/bucket

restful最佳实践——接口规范

其他

  • 排序

使用数组传递排序字段,-表示降序,无任何标识表示升序。

sorts: [‘-age’, ‘name’]

  • 时间传递

日期和时间戳如果没有适当和一致地处理,可能是一个真正的头痛。建议使用UTC或GMT时间存储,处理,缓存等时间戳或者使用统一格式化的时间字符串”yyyy-MM-dd HH:mm:ss”

Respone

状态码

restful最佳实践——接口规范

格式

  • 前后端交互字段全部使用小驼峰方式

// HTTP响应码(好多javascript框架并不会获取http状态码,所以包装到body中便于使用)
{ “code”: “200”, “status”: “success/fail/error”, // 见下述表格 “content/data”: []/{}, // 多条记录使用JSON数组,单条记录使用JSON对象 “message”: [] // 状态为error或fail时,对应的错误信息}

status说明

restful最佳实践——接口规范

示例

图表、下拉列表框

  • 图表、下拉列表框等建议统一key-name-value形式返回,这样对于图表来说可以统一处理,无需考虑业务性,增加了其复用性!

GET http://xxx.com/api/dashboard/se-count/:uid

{ “status”: “success”, “content”: [ { “key”: “low”, // 前后端交互使用关键字 “name”: “低级”, // 前端显示 “value”: 540 }, { “key”: “medium”, “name”: “中级”, “value”: 336 }, { “key”: “high”, “name”: “高级”, “value”: 92 } ], errorCode: “”, message: “”}

多条曲线

“status”: “success”, “content”:[ { “key”: “firewall”, “name”: “Firewall”, “value”: [ { “key”: 1508083200000, “name”: “3:00”, “value”: 23 }, { “key”: 1508094000000, “name”: “6:00”, “value”: 43 } ] }, { “key”: “vpn”, “name”: “VPN”, “value”: [ { “key”: 1508083200000, “name”: “3:00”, “value”: 31 }, { “key”: 1508094000000, “name”: “6:00”, “value”: 33 } ] } ]}

表格分页

请求

POST http://xxx.com/api/dashboard/se-by-source-ip

{ startTime: 1505977365777, endTime: 1506063765777, pageNum: 1, // 当前页码 pageSize: 20, // 每页条数 }

响应结果

{ “total”: 1, // 总条数 “totalPage”: 1, // (可选)总页数 “pageNum”: 1, // (可选)当前页码 “pageSize”: 20, // (可选)每页条数 “value”: [ { “field1”: “value”, “field2”: “value”, “field3”: “value”, “field4”: “value” } ]}

需要处理的问题

  • Get请求参数过多,或携带敏感信息
  • 批量删除,携带一组id信息
  • 文件导出、文件上传