RESTful API 设计规范
A Good API
Provides A Good Mental Model
考虑API的设计者、维护者、使用者在API生命周期内的互动:设计者应提供清晰的、易于理解的实体模型;维护者和使用者应对API有清晰的、一致的认识,以便对API的理解、调试、测试、扩展、维护。Simple
应避免在API中引入过多实现细节,或过度简化(避免出现简单调用也和复杂业务逻辑耦合的情况,以防暴露过多业务逻辑)。Allows Multiple Implementations
如果一个 API 自身可以有多个完全不同的实现,一般来说这个 API 已经有了足够好的抽象,也不会和外部系统耦合过紧。不同实现面向不同请求,在工程中可以采用不同的服务实例解决,通过访问不同endpoint配置来区分。
Well Documented
提供详细的API 文档
可以提升团队整体的研发效率。API 的设计者和维护者应准确记录每个字段和方法,并保持文档更新。
内容包括但不限于:
- 资源说明
- 字段和参数说明:边界值,默认值,字符串允许的字符范围及编码方式,示例值,字段说明(必须/仅限输入/仅限输出)。
- 方法说明:指明方法的效果及操作的资源。
- 使用惯例
Resource Model
资源(Resource)
是对一套 API 操作核心对象的抽象,设计 API 的一个重要前提是对资源本身合理定义。这种合理抽象应去除细节,具体实现留给具体系统处理。API 需要选择“对的”而不仅仅是简单的实体进行建模。
另外,不同层级
的抽象方式可能在描述同一资源,但在概念上是面向不同层面的选择的。服务中数据对象的多层处理要求使用针对不同层的数据模型
;便于明确服务本身的职责,也使API易于演进和维护。
Naming and Identification
API 的用词描述是云服务展现给外部用户的第一印象。
HTTP 协议中,最广为认知的是对 HTTP Mehod
的约定,使用9个单词(GET, HEAD, POST, PUT, DELETE, CONNECT, OPTION, TRACE)就完成了对基本动作的规范,为开发者提供了清晰的思维模型。同样,在 HTTP Header
里面也对浏览器信息、语言、网络连接属性等做了详细规定。这样开发者在使用 HTTP 服务时有一个大致约定,在关键信息上无偏差,保障了异构系统的接口一致性
。因此,云服务在管理API 时应该考虑一些具体的规范,对命名规则、标准词汇、最佳实践模式、错误码等信息都有明确的规定,便于用系统化、平台化的手段管理API。
一般有两种暴露给用户的命名(Naming)
/标识(Identification)
方式,实际操作时按需选择。
注:如果已有字符串或结构化的标识,则不一定需要数字ID(慎用数字ID)。
注:API中暴露给用户的公共部分,应体现其用途,而非实现。
- 采用
free-form string
- 优点:具体实现时自由度较大。
- 缺点:命名的内在结构不属于API定义的一部分,而是属于实现细节;泄露实现细节可能导致安全性问题。
- 采用
结构化数据
- 优点:如果资源对象的抽象模型本身包含结构化的标识信息,采用结构化的方式会简化客户端与资源的交互逻辑。这部分的业务逻辑是被描述系统的内在逻辑,而非实现细节;因此有助于简化具体实现,并且可以避免非结构化字符串带来的安全性问题。
- 缺点:不同系统的具体实现可能导致已定义的结构不适用,标识也不具有灵活性。
Operations
实际业务中,操作并不总是CRUD(CREATE, READ, UPDATE, DELETE)的;针对具体的对象/资源需要定义语义和行为概念上合理的相应操作。
允许API 设计者自定义操作来实现无法映射到标准方法的功能。如果API 功能能够自然映射到标准方法,则应在API 设计中使用该方法。
- 注:自定义并不意味着创建自定义HTTP 动词来支持自定义方法;对基于 HTTP 的 API 而言,映射到HTTP 动词最合适。
Idempotency
具备幂等性的操作可以多次进行且不会影响到第一次的结果。
- CREATE 类型的幂等性
多次调用容易产生重复创建,解决方案是在客户端生成唯一ID。
在反复重试创建时使用这个ID,便于服务端识别重复创建操作。 - UPDATE 类型的幂等性
更新值存在两种语义:更新增量,和设置新的总量。后者更易具备幂等性;而前者在多个客户请求同时增加时更易于并行处理,此时应考虑使用唯一token解决更新值的幂等性。 - DELETE 类型的幂等性
重复删除已被删除的对象可能会因为数据无法找到而报错。严格意义上不幂等,但操作无副作用。
Compatibility
API的变更应支持向后兼容
,确保客户端的使用不会被服务端的新版本中断。
实际中还应考虑如何做不兼容的API 变更。例如,删除一个方法/字段/枚举变量数值,方法/字段改名,方法/字段不做改变但其语义和行为发生变化(客户通常依赖API 语义和行为,即使此类行为没有得到明确记录)。
注:不兼容的变更需要通过 Deprecation Process
,在大版本发布时分步骤实现。
注:任何向后兼容的保证只适用于公共接口。因此区分公共接口和内部接口很重要。
变更参考
- 向后兼容的(非重大)变更
- 向 API 服务添加 API 接口
- 向 API 接口添加方法
- 向方法添加 HTTP 绑定
- 向请求消息添加字段
- 向响应消息添加字段
- 向枚举添加值
- 添加仅限输出的资源字段
- 向后不兼容的(重大)变更
- 移除或重命名服务、接口、字段、方法或枚举值
- 更改 HTTP 绑定
- 更改字段的类型
- 更改 proto 字段编号
- 更改资源名称格式
- 更改现有请求的可见行为
- 更改 HTTP 定义中的网址格式
- 向资源消息添加读取/写入字段
API Design
以 API 为中心完善相关体系,保障用户体验的一致性、及时性、稳定性、易用性。
API 是后端服务的外部表达。对于云服务商来说,应考虑选择支持哪种 API 风格才能更好地体现业务特性,让客户操作更方便。在选定具体模式后,要努力做到统一。
开发API 时应区分不同场景,做好元数据(Metadata)的管理。针对 API 是否开放要有明确的规范和度量机制,确保该开放的API 都可以开放、内外客户看到的功能集合基本一致,才能有利于云服务的更好集成。
以鉴权为例。权限在云平台中心一般是中心式管理;如果统一处理鉴权,就需要知道当前 API 正在操作什么资源,以及与此相关的资源都有哪些,然后针对相关资源逐一鉴权才能确认 API 操作是否可行。上述资源有两个关键点:一是要有统一的资源模型
,便于云产品对特定的 API 鉴权;二是要明确资源关系
,当出现资源依赖的时候,需要关联鉴权。
RPC(Remote Procedure Call)
:本地调用远程,是面向过程(Procedures, 也称为Functions;实体被隐藏在过程中)的调用。- API 组织形态为
领域
和行为
(对象和方法)。 - 即便是RPC 风格的API,也要大致符合HTTP 规则,否则可能会给一些依赖HTTP Code的系统造成误导。
- 方便客户端和服务器的本地编程;效率高,易于调用(类似库的调用)。但在软件后续需要技术/功能的改变及集成新服务的情况下灵活性较差。
- 命名多为动词;URL没有约定规范,需要详细文档说明。
- 支持的HTTP方法基本只用GET和POST。
- RPC风格的API是偏向内部的API。
- 适用目标:在用户管理的分布式部分之间需要交互,并且重点考虑处理效率时,建议使用RPC风格。
- API 组织形态为
REST(Representational State Transfer)
:面向资源的调用。- API组织形态为
资源
和实体
,以资源为中心(设计时需要明确API提供的资源,资源间的关系和导航,资源URL结构,资源的结构体;一个URL代表一种资源)。 - REST风格的API一般靠响应码来告知用户有什么问题;HTTP协议本身对错误码做出了详尽的规定,REST API要尽可能符合标准。
- API需要有一致的接口来解耦(
Decoupling
)客户端和服务实现,便于后续集成新功能。 - 命名API时暴露给用户的公共部分,应体现资源,而非动作。
- 支持的HTTP方法包括GET, POST, PUT, DELETE。
- REST风格的API是偏向外部的API。
- 适用目标:在需要通过分解成为多个组成部分(相互之间独立更佳)来提高软件的延展性时,或需要开放系统以考虑未来集成其他团队的项目时,建议使用REST风格。这样任何从标准文档了解过HTTP的人都可以使用你的API,并且无需阅读过多API 实体模型文档和请求语法说明。
- API组织形态为
Error-handling
错误返回机制是用户使用API时的重要组成部分。最佳实践建议使用标准、统一的错误码(Error Code)
,如 HTTP 的规范错误码,而不是由每个API自建一套 Error Code 和返回错误机制。
客户端最关心的是当前error是否该重试,还是应该继续向上层返回错误。如果只传递错误信息,使用 Error message
里的字段即可,无需自定义更多错误码。
REFERENCE
[1] 阿里云 - API设计最佳实践的思考:https://yq.aliyun.com/articles/701810?spm=a2c4e.11153940.0.0.391b7163Hh6IrB
[2] Google Cloud - API Design patterns: https://cloud.google.com/apis/design/design_patterns
[3] Microsoft - API design best practices: https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design
[4] HTTP status code: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes