REST
有一系列规范,满足这些规范的 API
均可称为 RESTful API
。REST
规范把所有内容都视为资源,也就是说网络上一切皆资源。REST
架构对资源的操作包括获取、创建、修改和删除,这些操作正好对应 HTTP
协议提供的 GET
、POST
、PUT
和 DELETE
方法。HTTP
动词与 REST
风格 CRUD
的对应关系见下表:
REST
具有以下核心特点:
resource
) 为中心,所有的东西都抽象成资源,所有的行为都应该是在资源上的 CRUD
操作。URI
标识,每个资源实例都有一个唯一的 URI
标识。例如,如果我们有一个用户,用户名是 admin
,那么它的 URI
标识就可以是 /users/admin
。JSON/XML
等在 HTTP Body
里表征资源的状态。HTTP
动词,对服务器端资源进行操作,实现“表现层状态转化”。RESTful API
请求都包含了所有足够完成本次操作的信息,服务器端无须保持 session
。无状态对于服务端的弹性扩容是很重要的。REST
和 RESTful API
的区别:REST
是一种规范,而 RESTful API
则是满足这种规范的 API
接口。
URI
设计时,应该遵循的一些规范:
Collection
和 Member
两种。Collection
:一堆资源的集合。例如我们系统里有很多用户(User
), 这些用户的集合就是 Collection
。 Collection
的 URI
标识应该是 域名/资源名复数, 例如 https:// iam.api.marmotedu.com/users
。Member
:单个特定资源。例如系统中特定名字的用户,就是 Collection
里的一个 Member
。Member
的 URI
标识应该是 域名/资源名复数/资源名称, 例如 https:// iam.api.marmotedu/users/admin
。URI
结尾不应包含 /
。URI
中不能出现下划线 _
,必须用中杠线 -
代替。URI
路径用小写,不要用大写URI
。超过 2 层的资源嵌套会很乱,建议将其他资源转化为 ?
参数,比如:/schools/tsinghua/classes/rooma/students/zhang # 不推荐
/students?school=qinghua&class=rooma # 推荐
这里有个地方需要注意:在实际的 API
开发中,可能你会发现有些操作不能很好地映射为一个 REST
资源,这时候,你可以参考下面的做法。
URI:/users/zhangsan?active=false
。GitHub
的加星操作:PUT /gists/:id/star # github star action
DELETE /gists/:id/star # github unstar action
URI
可以设计为:/login
。基本上 RESTful API
都是使用 HTTP
协议原生的 GET
、PUT
、POST
、DELETE
来标识对资源的 CRUD
操作的,形成的规范如下表所示:
在使用 HTTP
方法的时候,有以下两点需要你注意:
GET
返回的结果,要尽量可用于 PUT
、POST
操作中。例如,用 GET
方法获得了一个 user
的信息,调用者修改 user
的信息,然后将此结果再用 PUT
方法更新。这要求 GET
、PUT
、POST
操作的资源属性是一致的。PUT
方法,POST
方法仅用来创建或者批量删除这两种场景。在设计 API
时,经常会有批量删除的需求,需要在请求中携带多个需要删除的资源名,但是 HTTP
的 DELETE
方法不能携带多个资源名,这时候可以通过下面三种方式来解决:
DELETE
请求。id
,id
之间用分隔符分隔, 例如:DELETE /users?ids=1,2,3
。POST
方式来批量删除,body
中传入需要删除的资源列表。其中,第二种是我最推荐的方式,因为使用了匹配的 DELETE
动词,并且不需要发送多次 DELETE
请求。
你需要注意的是,这三种方式都有各自的使用场景,你可以根据需要自行选择。如果选择了某一种方式,那么整个项目都需要统一用这种方式。
一般来说,一个系统的 RESTful API
会向外界开放多个资源的接口,每个接口的返回格式要保持一致。另外,每个接口都会返回成功和失败两种消息,这两种消息的格式也要保持一致。不然,客户端代码要适配不同接口的返回格式,每个返回格式又要适配成功和失败两种消息格式,会大大增加用户的学习和使用成本。
API
版本有不同的标识方法,在 RESTful API
开发中,通常将版本标识放在如下 3 个位置:
URL
中,比如 /v1/users
。HTTP Header
中,比如 Accept: vnd.example-com.foo+json; version=1.0
。Form
参数中,比如 /users?version=v1
。有些开发人员不建议将版本放在 URL
中,因为他们觉得不同的版本可以理解成同一种资源的不同表现形式,所以应该采用同一个 URI
。对于这一点,没有严格的标准,根据项目实际需要选择一种方式即可。
API
通常的命名方式有三种,分别是驼峰命名法 (serverAddress
)、蛇形命名法 (server_address
) 和脊柱命名法 (server-address
)。
驼峰命名法和蛇形命名法都需要切换输入法,会增加操作的复杂性,也容易出错,所以这里建议用脊柱命名法。
REST
资源的查询接口,通常情况下都需要实现分页、过滤、排序、搜索功能,因为这些功能是每个 REST
资源都能用到的,所以可以实现为一个公共的 API
组件。下面来介绍下这些功能。
Collection
下所有的 Member
时,应该提供分页功能,例如 /users?offset=0&limit=20
(limit
,指定返回记录的数量;offset
,指定返回记录的开始位置)。引入分页功能可以减少 API
响应的延时,同时可以避免返回太多条目,导致服务器 / 客户端响应特别慢,甚至导致服务器 / 客户端 crash
的情况。URI
参数里指定返回哪些属性,例如 /users?fields=email,username,address
。Collection
中前 100 个 Member
,这时可以在 URI
参数中指明排序参数,例如 /users?sort=age,desc
。Member
太多时,用户可能想通过搜索,快速找到所需要的 Member
,或着想搜下有没有名字为 xxx
的某类资源,这时候就需要提供搜索功能。搜索建议按模糊匹配来搜索。API
的域名设置主要有两种方式:
https://marmotedu.com/api
,这种方式适合 API
将来不会有进一步扩展的情况,比如刚开始 marmotedu.com
域名下只有一套 API
系统,未来也只有这一套 API
系统https://iam.api.marmotedu.com
,如果 marmotedu.com
域名下未来会新增另一个系统 API
,这时候最好的方式是每个系统的 API
拥有专有的 API
域名,比如:storage.api.marmotedu.com
,network.api.marmotedu.com
。腾讯云的域名就是采用这种方式。这里有个需要注意的点:不同公司、不同团队、不同项目可能采取不同的 REST
设计原则,以上所列的基本上都是大家公认的原则。