关于RESTful API 设计的总结

为何要用 RESTful

RESTful 给个人最大感受就是规范、易懂和优雅,一个结构清晰、易于理解的 API 彻底能够省去许多无心义的沟通和文档。而且 RESTful 如今愈来愈流行,html

在开始介绍 RESTful API 以前,先介绍一下 RESTful 架构。api

RESTful 架构

REST,即Representational State Transfer 的缩写。意为 " 表现层状态转化 " 。缓存

要理解RESTful架构,最好的方法就是去理解 Representational State Transfer 这个词组究竟是什么意思,它的每个词表明了什么涵义。若是把这个名称搞懂了,也就不难体会 REST 是一种什么样的设计。服务器

资源 (Resources)

REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。网络

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它能够是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你能够用一个 URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的 URI 就能够,所以 URI 就成了每个资源的地址或独一无二的识别符。所谓"上网",就是与互联网上一系列的"资源"互动,调用它的 URI 。架构

表现层(Representation)

"资源"是一种信息实体,它能够有多种外在表现形式。咱们把"资源"具体呈现出来的形式,叫作它的"表现层"(Representation)。
好比,文本能够用 txt 格式表现,也能够用 HTML 格式、 XML 格式、JSON 格式表现,甚至能够采用二进制格式;图片能够用 JPG 格式表现,也能够用 PNG 格式表现。
URI 只表明资源的实体,不表明它的形式。严格地说,有些网址最后的" .html "后缀名是没必要要的,由于这个后缀名表示格式,属于"表现层"范畴,而 URI 应该只表明"资源"的位置。它的具体表现形式,应该在 HTTP 请求的头信息中用 Accept 和 Content-Type 字段指定,这两个字段才是对"表现层"的描述。性能

状态转化(State Transfer)

访问一个网站,就表明了客户端和服务器的一个互动过程。在这个过程当中,势必涉及到数据和状态的变化。
互联网通讯协议 HTTP 协议,是一个无状态协议。这意味着,全部的状态都保存在服务器端。所以,若是客户端想要操做服务器,必须经过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是创建在表现层之上的,因此就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来讲,就是HTTP协议里面,四个表示操做方式的动词:GET 、 POST 、 PUT 、 DELETE 。 它们分别对应四种基本操做: GET 用来获取资源, POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。网站

综述

总结一下什么是RESTful架构:加密

  • 每个URI表明一种资源;
  • 客户端和服务器之间,传递这种资源的某种表现层;
  • 客户端经过四个HTTP动词,对服务器端资源进行操做,实现"表现层状态转化"。

RESTful API 的设计

介绍完 RESTful 的含义,再说说 RESTful API 的设计。设计

协议

若是能全站 HTTPS 固然是最好的,不能的话也请尽可能将登陆、注册等涉及密码的接口使用 HTTPS。

域名

应该尽可能将API部署在专用域名之下。如:

https://api.example.com

若是肯定API很简单,不会有进一步扩展,能够考虑放在主域名下。

https://example.org/api/

版本号

API 的版本号和客户端 APP 的版本号是毫无关系的,不要让 APP 将它们用于提交应用市场的版本号传递到服务器,而是提供相似于v一、v2之类的 API 版本号。

版本号拼接在 URL 中。如:

api.example.com/v1/users

或是放在 Header 中:

api.xxx.com/users

version=v1

请求

通常来讲 API 的外在形式无非就是增删改查(固然具体的业务逻辑确定要复杂得多),而查询又分为详情和列表两种,在 RESTful 中这就至关于通用的模板。例如针对文章(Article)设计 API,那么最基础的 URL 就是这几种:

  • GET /articles: 文章列表
  • GET /articles/id:文章详情
  • POST /articles/: 建立文章
  • PUT /articles/id:修改文章
  • DELETE /articles/id:删除文章

Token 和 Sign

API 须要设计成无状态,因此客户端在每次请求时都须要提供有效的 Token 和 Sign,在我看来它们的用途分别是:

  • Token 用于证实请求所属的用户,通常都是服务端在登陆后随机生成一段字符串(UUID)和登陆用户进行绑定,再将其返回给客户端。Token 的状态保持通常有两种方式实现:一种是在用户每次操做都会延长或重置 TOKEN 的生存时间(相似于缓存的机制),另外一种是 Token 的生存时间固定不变,可是同时返回一个刷新用的 Token,当 Token 过时时能够将其刷新而不是从新登陆。
  • Sign 用于证实该次请求合理,因此通常客户端会把请求参数拼接后并加密做为 Sign 传给服务端,这样即便被抓包了,对方只修改参数而没法生成对应的 Sign 也会被服务端识破。固然也能够将时间戳、请求地址和 Token 也混入 Sign,这样 Sign 也拥有了所属人、时效性和目的地。

业务参数

在 RESTful 的标准中,PUT 和 PATCH 均可以用于修改操做,它们的区别是 PUT 须要提交整个对象,而 PATCH 只须要提交修改的信息。可是在我看来实际应用中不须要这么麻烦,因此我一概使用 PUT,而且只提交修改的信息。

另外一个问题是在 POST 建立对象时,究竟该用表单提交更好些仍是用 JSON 提交更好些。其实二者均可以,在我看来它们惟一的区别是 JSON 能够比较方便的表示更为复杂的结构(有嵌套对象)。另外不管使用哪一种,请保持统一,不要二者混用。

还有一个建议是最好将过滤、分页和排序的相关信息全权交给客户端,包括过滤条件、页数或是游标、每页的数量、排序方式、升降序等,这样可使 API 更加灵活。可是对于过滤条件、排序方式等,不须要支持全部方式,只须要支持目前用得上的和之后可能会用上的方式便可,并经过字符串枚举解析,这样可见性要更好些。例如:

搜索,客户端只提供关键词,具体搜索的字段,和搜索方式(前缀、全文、精确)由服务端决定:

/users/?query=ScienJus

过滤,只须要对已有的状况进行支持:

/users/?gender=1

分页:

/users/?page=2&pre_page=20

响应

尽可能使用 HTTP 状态码,经常使用的有:

  • 200:请求成功
  • 201:建立、修改为功
  • 204:删除成功
  • 400:参数错误
  • 401:未登陆
  • 403:禁止访问
  • 404:未找到
  • 500:系统错误

可是有些时候仅仅使用 HTTP 状态码没有办法明确的表达错误信息,因此也能够在里面再包一层自定义的返回码,例如:

成功时:

{
    "code": 100,
    "msg": "成功",
    "data": {}
}
{
    "code": -1000,
    "msg": "用户名或密码错误"
}

data是真正须要返回的数据,而且只会在请求成功时才存在,msg只用在开发环境,而且只为了开发人员识别。客户端逻辑只容许识别code,而且不容许直接将msg的内容展现给用户。若是这个错误很复杂,没法使用一段话描述清楚,也能够在添加一个doc字段,包含指向该错误的文档的连接。

返回数据

JSON 比 XML 可视化更好,也更加节约流量,因此尽可能不要使用 XML。

建立和修改操做成功后,须要返回该资源的所有信息。

返回数据不要和客户端界面强耦合,不要在设计 API 时就考虑少查询一张关联表或是少查询 / 返回几个字段能带来多大的性能提高。而且必定要以资源为单位,即便客户端一个页面须要展现多个资源,也不要在一个接口中所有返回,而是让客户端分别请求多个接口。

最好将返回数据进行加密和压缩,尤为是压缩在移动应用中仍是比较重要的。