类似于阿里巴巴的开发规范或者谷歌的开发规范,API设计是目前很多场景下的基本功,所以这里给出一个笔者的最佳实践(Best Practice).
大纲
- 引言
- 基本的设计原则
- 良好的工具和选型
- API的基础
- 路径和参数设计
- 选择与权衡
- 其他最佳实践
- 更多的约定
- 一个文档规范的示范
- 参考文献
引言
REST,即Representataional State Transfer的缩写。关于RESTful架构,可以参考《架构之美》
中的定义。
- 客户端和服务器之间的交互在请求间是无状态的,每个请求都必须包含理解请求的全部信息。
- 在此基础上,服务更容易实现分布式,水平扩展,异步处理和可重入(幂等请求)
REST架构中的基本定义
- 资源(Resource): 网络上的所有内容都表述成一个资源,一个实体或者一个具体的信息。它可以是一段文本,一张图片,一首歌曲,或者一种服务。
- 统一资源定位符(URI, Universal Resource Identifier): 一个资源的识别符或者说是一个地址,通过URI可以定位到一个唯一的资源,网络上每个资源都有一个唯一的识别符。
- 状态转换(State Transfer): 所有资源都共享统一的接口,以便在客户端和服务器之间传输状态。客户端与服务器端互动的过程,通常涉及到服务器端数据和状态变化的过程,比如文件被修改,访问数量增加等。
- 使用基本的HTTP请求方法
- GET: 获取资源(幂等并且安全的)。
- POST: 新建资源
- PUT: 更新资源
- DELETE: 删除动作
- Hypermedia: 应用程序状态的引擎,实现hypermeida是API的最佳实践之一,可以参考api.github.com,后文也会再讲到。
基本的设计原则
协议(Protocol)
API与客户端通信的协议,应该尽量使用HTTPs。除此之外,白名单机制,VPN可以提高更多安全性。遵循“所有人应该知道他所需要完成工作必备的最少的知识”的原则。而一个接口,只有在访问者必须使用它时,才告诉这个访问者。
域名(Domain)
应该将API部署到专用域名之下。好处显而易见。
- 更容易做动静分离。
- 更容易做服务降级和限流。
- 更容易做到高吞吐量。
- 更容易做流量分发。
- 更容易进行之后的水平扩展和服务拆分。
Sample: https://api.groupname.domain.io/
更好的实践是,将服务分组,并且根据情况进行必要的层次和分组(group)。这里的group可以包含事业部,也可以包含不同的客户,更可以包含不同的层次。
规范: https://api.[service-group].domain.io/
基本URL(Root URL)
基本的URL,在此文中指的是除了服务拆分之外的URL,这里的最佳实践是,可以包含环境,版本,分类,层次等,但是一个基本的URL,已经能决定除了接口或者服务粒度以外的所有事情。或者说,他可以决定,由一个作战单元(个人或者小的敏捷团队)日常维护的工作内容了。
Sample: https://api-dev.groupname.domain.io/mobile/v1/comment/[...]
- 此处dev表示开发环境(这里的api默认表示生产,api-dev表示开发,职责仍然是唯一的)
- groupname表示分组(可以包含更多层次信息或者更多分层,但是最佳实践是一般domain不超过两层,也可以默认约定.io域名后缀全部是为api部署服务)
- mobile表示面向移动端
- v1表示本类的接口版本
- comment表示此本类服务为评论服务
环境划分原则(Env)
整体服务架构的划分原则不在此文档讨论范围之内,而一个最佳实践是,在domain当中就对环境做区分。
规范: https://api-[envname].groupname.domain.io/
接口的版本(API Version)
应该将API的版本号放入URL合适的层次。注意这里的Version即不表示客户端的版本,亦不表示服务器中服务的对应版本,而是特指该接口的版本。一般用来处理对接口做升级的情况。
规范: https://api-[env-name].groupname.domain.io/mobile/[version]/
- v1和v2的区别,应该表示且仅表示接口的区别。
- 不要发布无版本号的接口。
- 使用简单的数字。
- 服务分组加版本两个变量来共同决定接口的实现逻辑。
路径(EndPoints,端点)命名
此原则仅供参考。准备重写。
路径表示API的具体URL,每个URL唯一的表示一种资源。所以网址中不应该有动词,只应该有名词。而且所用的名词往往与代表的对象名称对应,一般来说某一种记录的集合,所以API名词当中应该使用复数。
- URL: /cards/getCardById/{id} => HTTP GET: /cards/{id}
- 如果某些动作是HTTP动词表达不了的,那么应该把动作当成资源去处理。
- POST /accounts/1/transfer/500/to/2/ => POST /transaction?from=1&to=2&amount=500.00
- 为了保持简单,只对所有资源使用复数。
- /setting => /settings
- /user => /users
- 资源之间的层级关系应该表述清楚。
- GET /hotels/1312/homes/ 返回酒店1312的所有房间。
- GET /hotels/1312/homes/1209 返回酒店1312的1209房间。
HTTP动词(HTTP Verbs)
- GET (SELECT): 从服务器取出对应的资源。
- POST (CREATE): 新建一个资源。
- PUT (UPDATE): 在服务器更新资源。(客户端提供改变后的完整的资源,所以应该少用)
- PATCH (UPDATE): 在服务器更新资源。(客户端提供改变的属性)
- DELETE (DELETE): 删除资源。
- HEAD: 获取资源的元数据。
- OPTIONS: 获取信息,关于哪些资源的哪些属性是客户端可以改变的。
最佳实践
- 此处的最佳实践是,大家约定好我们的动作处理原则,并且在整个系统(组织或者公司)内保持统一。
- GET方法不应该涉及状态改变。
- 很多时候我们还需要收集客户端的信息,但是REST本身是无状态的,所以收集的时候,应该独立于REST API的设计原则,独立设计体系来收集类似于Client, cookie, ip和device等信息。
不符合CRUD的情况
一般有如下建议
- 使用POST 重构行为的action
- 增加控制参数(整体来约定)
- 把动作转换成资源
过滤信息(Filtering)
API应该提供参数,过滤返回结果。因为服务器端某个资源数量可能很多。比如用户的订单数,全国的酒店数等。过滤的语义应该包括对数据集合的过滤,排序,选择,和分页等功能。
- ?limit=10: 返回指定条目的数据。
- ?offset=10: 指定返回记录的开始位置。
- ?pageNumber=2&perSize=100: 指定第几页,以及每页的记录数量。
- ?sortBy=time&order=desc: 排序顺序及属性。
- ?type=1: 指定筛选条件。
最佳实践:
- 实际上,我们在处理API业务时,不可能像数据库查询那么简洁容易。所以参数定义应该更加单一职责,更加严谨。
- 总是可以在输入参数中设计一个客户端需要的attrList, 由使用者来指定需要的属性列表。
- 参数的设计上应该允许冗余。
- 可以使用HTTP的定制头: X-Total-Count表示资源总数。
状态码(Status Codes)
- 这里一般实践是包含两层,一层是中间件(比如网关,nginx,tomcat)返回的HTTP请求本身的状态码。另外还包含服务本身返回的状态码设计。
- 这里需要单独开辟一个章节来定义状态码,后文会给出一个最佳实践。
- 一个好的可以坚持的原则是,服务本身返回的状态码的前缀,应该和网关那一层保持一致。这样可以有最好的层次关系。.
- 比如40001表示请求参数错误,其中400表示INVALID REQUEST。01表示具体的错误为参数错误。
错误处理(Error Handling)
如果状态码不是正确的返回,就应该返回出错信息。尽量使用详细的错误信息。
一个好的实践是,出错信息应该包含:
userMessage: 显示给用户的。
internalMessage: 显示给程序员调试用的。
code: 编码
guideline: 参考解决指南。
返回结果(Response)
- 按照RESTful架构“宽进严出”的原则,返回应该被严格定义。
- 全部使用JSON返回结构。
- 基本约定:
- GET /collection: 返回资源列表
- GET /collection/resource: 返回单个对象
- POST /collection/resource: 返回新生成的对象。
- PUT /collection/resource: 返回完整的更新后的资源对象。
- PATCH /collection/resource: 返回完整的更新后的资源对象。
- DELETE /collection/resource: 返回一个空文档。
- 实际执行过程中远比这个复杂,但是我们仍然可以有一些基本原则。
使用HATEOAS构建Hypermedia APIs
超媒体API很可能是RESTful API设计的未来。它们实际上是一个非常惊人的概念,可以追溯到HTTP和HTML的工作原理。我们可以使用HATEOAS in Spring来构建Hypermedia APIs,而在此之前,约定更加重要。
一个好的Hypermedia范例是 api.github.com
1 | { |
标准请求定义
标准的请求定义当中有很多最佳实践,比如Content-Type等等。好的实践是,我们早早的用很小的代价把content-type, language这些加入设计当中,可以避免后续很多问题。后文会给出最佳实践。我们也需要看看,HTTP请求当中,到底包含了哪些东西。
认证(Authentication)
认证的时候取决于API的使用者和生产者之间的关系,以及需要保护的程度。目前此处的最佳实践是采用OAuth 2.0当中合适的模式来构建。
文档(Documentation)
使用Swagger API+JSON,进行文档管理和信息描述。定义一个标准的,语言无关的,供人和计算机理解服务的文档。类似于SOAP当中的WSDL。
- 需要满足API自动生成同步的在线文档。
- 可以用于API设计review。
- 方便测试人员了解API定义。
- 可以作为客户产品文档的一部分进行发布。
- 可以通过API Swagger文档生成使用者和生产者的骨架代码。
- 由于API的灵活性,此处一般不做严格要求。
一个文档规范示范
版权声明
本文标题:86-RestAPI设计指南-1-引言
文章作者:盛领
发布时间:2019年01月13日 - 10:35:21
原始链接:http://blog.xiaoyuyu.net/post/46676be6.html
许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。
如您有任何商业合作或者授权方面的协商,请给我留言:sunsetxiao@126.com