

REST API 设计规范
source link: https://www.cyningsun.com/06-29-2020/how-to-write-restful-api.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在项目中往往会需要确定一个好的API风格,到底有哪些风格可以参考,API Style 的细节要点有哪些呢?
Http API Style 有哪些?
- SOAP:tend to be centered around operations that are usually use-case specific and specialized.
- REST:centered around business (data) entities exposed as resources that are identified via URIs and can be manipulated via standardized CRUD-like methods using different representations, and hypermedia
- GraphQL:a query language for APIs and a runtime for fulfilling those queries with your existing data
SOAP 风格(严格来说,算不上风格)最早于1998年由微软提出;REST 风格于2000年 由 Roy Thomas Fielding 论文中提出;GraphQL 于2015年由 Facebook 提出;
- SOAP vs REST
如果要轻松、快速地完成API设计,SOAP 风格的API就足够了。毕竟REST有时很难做到,尤其是在一开始。但随着时间的推移,使用RESET 风格的API,服务器端的演进变得更容易,客户机对变化的适应能力也更强。
-
REST vs GraphQL
REST 限于其历史背景,对于
查询
操作一些细节并没有太多描述,随着互联网的发展,查询的复杂度越来越高,而 GraphQL 是一个很好的补充。
在业界有将近70%的API是REST-like的风格,其中当然就包括谷歌、微软等行业巨头,REST 差不多已经成为了事实上的标准,了解、用好 REST 十分必要。
REST 是什么?
REST,全称是 Resource Representational State Transfer:通俗来讲就是:资源在网络中以某种表现形式进行状态转移。分解开来:
- Resource:资源,即数据(前面说过网络的核心)。比如 newsfeed,friends等;
- Representational:某种表现形式,比如用JSON,XML,JPEG等;
- State Transfer:状态变化。通过HTTP动词实现。
资源模型
资源是具有类型、数据、与其他资源关系、以及一组对其进行操作的方法的对象。
Richardson成熟度模型
Level 0
不使用任何URI,HTTP方法和HATEOAS功能。
该模型的出发点是使用HTTP作为远程交互的传输系统,但不使用Web的任何机制。基本上就是使用HTTP作为你远程交互机中的隧道机制,通常基于“远程过程调用“(RPC, Remote Procedure Invocation )。
Level 1 - Resources
使用URI、HTTP方法、HATEOAS中的 URI 。
迈向REST的第一步就是引入资源的概念。接下来,我们所要讨论的是各个资源,而不是将所有请求发送到单一的服务端点。每个资源都由唯一的URI单独标识
Level 2 - HTTP Verbs
使用URI、HTTP方法、HATEOAS中 的URI和HTTP 。
支持每个公开资源上的几个HTTP谓词 - 创建,读取,更新和删除(CRUD)服务。通常代表业务实体的资源状态可以通过网络进行操作。
Level 3 - Hypermedia Controls
使用所有三个,即URI,HTTP和HATEOAS。
超媒体控制的关键在于它告诉我们下一步我们可以做什么,以及操作所需资源的URI。与我们必须提前知道在哪里创建预约请求不同(Level2中),在响应中的 HATEOAS 告诉了我们下一步该如何做,以完成应用程序状态转换。
关键问题
REST 本身不是标准,只是一种风格。因此只要遵从该风格,都是OK的。然而,除此之外我们逃不开使用中遇到的很多问题,最典型的问题,如下:
- Error Handling
- Sorting
- Pagination
- versioning
- filtering
- Long running
- Sub-collection
- Action(i.e. Batch Operation)
Error handling
详见:跨服务错误处理
Sorting
如果 API 方法允许客户端指定列表结果的排序顺序,则请求消息 应该 包含一个字段:
string order_by = ...;
说明:语法中的冗余空格字符是无关紧要的。 "foo,bar desc"
和 " foo , bar desc "
是等效的。
字符串值 应该 遵循 SQL 语法:逗号分隔的字段列表。例如: "foo,bar"
。默认排序顺序为升序。要将字段指定为降序, 应该 将后缀 " desc"
附加到字段名称中。例如: "foo desc,bar"
。
Pagination
- 可列表集合 应该 支持分页,即使结果通常很小。
说明:如果某个 API 从一开始就不支持分页,稍后再支持它就比较麻烦,因为添加分页会破坏 API 的行为。 不知道 API 正在使用分页的客户端可能会错误地认为他们收到了完整的结果,而实际上只收到了第一页。
-
翻页方式
后台存储 Request Response 搜索引擎 {
“page_num”: 1, // 页码从 1 开始
“page_size”: 10
} {
“code”: 0,
“msg”: “”,
“data”: {
“total_cnt”: 100,
“items”: []
}
} 数据库 {
“last_id”: 1, // 第一页,默认传0
“page_size”: 10
} {
“code”: 0,
“msg”: “”,
“data”: {
“items”: [],
“next_id”: 10 // 下一页放到last_id的值
}
}
Versioning
业界方案
-
无版本控制
这是最简单的方法,它对于一些内部 API 来说可能是可以接受的。 重大变化可以表示为新资源或新链接。 向现有资源添加内容可能不会带来重大更改,因为不希望看到此内容的客户端应用程序将忽略它。
https://adventure-works.com/customers/3
-
URI 版本控制
每次修改 Web API 或更改资源的架构时,向每个资源的 URI 添加版本号。 以前存在的 URI 应像以前一样继续运行,并返回符合原始架构的资源。
https://adventure-works.com/v2/customers/3
-
查询字符串版本控制
不是提供多个 URI,而是可以通过在追加到 HTTP 请求后面的查询字符串中使用参数来指定资源的版本,例如
https://adventure-works.com/customers/3?version=2
注意:某些较旧的 Web 浏览器和 Web 代理不会缓存在 URI 中包含查询字符串的请求的响应。 这可能会降低使用 Web API 并在此类 Web 浏览器中运行的 Web 应用程序的性能。
-
标头版本控制
不是追加版本号作为查询字符串参数,而是可以实现指示资源的版本的自定义标头。 此方法需要客户端应用程序将相应标头添加到所有请求,虽然如果省略了版本标头,处理客户端请求的代码可以使用默认值(版本 1)。 下面的示例使用了名为 Custom-Header 的自定义标头**。 此标头的值指示 Web API 的版本。
GET https://adventure-works.com/customers/3 HTTP/1.1 Custom-Header: api-version=1
-
无版本控制:在更改RESTful API时,请以兼容的方式进行更改,并避免生成其他API版本。
说明:多个版本会使理解、测试、维护、发展、操作和发布我们的系统变得非常复杂。
在更改RESTful api时,请以兼容的方式进行更改,并避免生成其他API版本。多个版本可能会显著地复杂化查看、测试、维护、发展、运营和发布系统( 补充阅读 )。如果无法以兼容的方式更改API,请使用这三种方式:
- 在旧资源变体的基础上创建新资源(变量)
- 创建一个新的服务端点-即一个具有新API的新应用程序(使用新域名)
- 在微服务中创建一个新的API版本,该版本支持与旧API同时支持
Filtering
在参数过滤时通常会为参数值定义数据格式。为了在所有 API 中提供一致的开发者体验并减少学习曲线,API 设计人员 必须 使用以下 扩展巴科斯范式 (Extended Backus-Naur Form,简写为“EBNF”)语法的变体来定义这样的语法:
Production = name "=" [ Expression ] ";" ; Expression = Alternative { "|" Alternative } ; Alternative = Term { Term } ; Term = name | TOKEN | Group | Option | Repetition ; Group = "(" Expression ")" ; Option = "[" Expression "]" ; Repetition = "{" Expression "}" ;
注意: TOKEN
表示在语法之外定义的终端符号。
Example
GET /zoos?id=1001,1002,1003
Long running
有时,POST、PUT、PATCH 或 DELETE 操作可能需要一段时间才能完成。如果需要等待该操作完成后才能向客户端发送响应,可能会造成不可接受的延迟。在这种情况下,请考虑将该操作设置为异步操作。返回 HTTP 状态代码 202(已接受),指示该请求已接受进行处理,但尚未完成。
应公开一个可返回异步请求状态的终结点,使客户端能够通过轮询状态终结点来监视状态。在 202 响应的 Location 标头中包含状态终结点的 URI。例如:
HTTP/1.1 202 Accepted Location: /api/status/12345
如果客户端向此终结点发送 GET 请求,响应中应包含该请求的当前状态。(可选)响应中还可以包含预计完成时间,或者用于取消操作的链接
HTTP/1.1 200 OK Content-Type: application/json { "status":"In progress", "link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" } }
如果异步操作创建了新资源,则该操作完成后,状态终结点应返回状态代码 303(查看其他)。在 303 响应中,包含一个 Location 标头用于提供新资源的 URI:
HTTP/1.1 303 See Other Location: /api/orders/12345
有关详细信息,请参阅 异步请求-回复模式 。
Sub-collection
有时,API 需要让客户跨子集执行 List/Search
操作。例如,“API 图书馆”有一组书架,每个书架都有一系列书籍,而客户希望在所有书架上搜索某一本书。在这种情况下,建议在子集合上使用标准 List
,并为父集合指定通配符集合 ID "-"
。对于“API 图书馆”示例,我们可以使用以下 REST API 请求:
GET https://library.googleapis.com/v1/shelves/-/books/{id}
注意:选择 "-"
而不是 "*"
的原因是为了避免需要进行 URL 转义。
Action
常用的HTTP动词有下面五个(括号里是对应的SQL命令)。
- GET(SELECT):从服务器取出资源(一项或多项)。
- POST(CREATE):在服务器新建一个资源。
- PUT(UPDATE):在服务器更新资源(客户端提供改变的属性)
- DELETE(DELETE):从服务器删除资源。
对于非标准的操作,以上动词无法无法满足需求,可以在资源上使用“操作”子集合。 动作基本上类似于RPC的消息,用于对资源执行特定操作。 “动作”子集合可以看作是一个命令队列,可以将新的动作发布到该命令队列中,然后由API执行。定义标准动词如下:
- batch:批量操作
- search:搜索操作
GET /zoos:列出所有动物园 POST /zoos:新建一个动物园 GET /zoos/ID:获取某个指定动物园的信息 PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息) PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息) DELETE /zoos/ID:删除某个动物园 GET /zoos/ID/animals:列出某个指定动物园的所有动物 DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物 GET /zoos/-/action/batch 批量查询 POST /zoos/-/action/batch 批量更新 POST /zoos/-/action/search 搜索
其他
-
对
required nullable {} {“example”:null}null
和不存在的属性使用相同的语义true
true
✗ No ✔ Yesfalse
true
✔ Yes ✔ Yestrue
false
✗ No ✗ Nofalse
false
✔ Yes ✗ No -
路径使用 中划线
-
代替 下划线_
;在搜索引擎中,把中划线当做空格处理,而下划线是被忽略的。使用中划线是对搜索引擎友好的写法
Example:
/shipment-orders/{shipment-order-id}
-
范围
表示范围的字段 应该 使用半开区间和命名惯例
[start_xxx, end_xxx)
,例如[start_key, end_key)
或[start_time, end_time)
。通常 C ++ STL 库和 Java 标准库会使用半开区间语义。API 应该 避免使用其他表示范围的方式,例如(index, count)
或[first, last]
。
总结
完成以上这些,也仅仅是达到REST Level 2,由于Level 3 对于API风格影响不大,暂不涉及。对 HATEOAS 感兴趣,可以参考 Github v3 版本的API。
参考链接
- Google Cloud API guide
- Microsoft API design best practices
- Zalando RESTful API and Event Scheme Guidelines
- OpenAPI-Specification
- Consumer-Centric API Design v0.4.0.pdf
- s3 rest api and post method
- what is hypermedia hypermedia controls hypermedia formats
- Enterprise Integration Using REST
本文作者:cyningsun
本文地址: https://www.cyningsun.com/06-29-2020/how-to-write-restful-api.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-ND 3.0 CN 许可协议。转载请注明出处!
Recommend
-
58
预备知识: 用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识
-
65
本文是为 大渝网 API 开发规范拟定的一个 beta 版,文章大量参考了目前比较常见的 RESETful API 设计。 关于「能愿动词」的使用 为了避免歧义,文档大量使用了「能愿动词」,对应的解释如下: 必须 (MUST):绝对,严格遵循,请照做,无条
-
38
SpaceX REST API Open Source REST API for rocket, core, capsule, pad, and launch data Docs See the
-
64
程序员 - @gzf6 - 楼主是做前端的,现在学着用 koa2 写 api 服务器,公司的后端接口一般都设计为 get 或 post 方法,很少用 put 或 delete 方法,想请教下,这两个方法有什么安全上的问题吗?
-
35
这篇文章分享 API 接口设计规范,目的是提供给研发人员做参考。 规范是死的,人是活的,希望自己定的规范,不要被打脸。 路由命名规范 动作 前缀 备注 获取...
-
21
免责声明:尽管标题有争议,但本文并不是试图证明RPC比REST更好,或者GraphQL比RPC更好。相反,本文的目的是向你介绍这些方法的大致情况以及它们的优缺点。最终的选择将会是一个权衡。 尽管 HTTP 是一个应用层(例如,L7)协议,但在 API 开发方面,H...
-
13
REST API Development with Flask Patterns for deploying RESTful Flask applications Software Engineering July 24, 2020
-
10
SharePoint REST API 获取文件夹下的项目数 正文 ...
-
16
API设计规范参考 2020.01.08API设计规范参考 2020.01.08零一间2021.01.12 11:12:29字数 0阅读 31
-
6
优秀的 REST API 设计指南发布于 2020年06月28日 | 上次编辑:2020年08月01日
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK