1

【API设计与开发实践】第2篇 Restful API 设计最佳实践的四个重要改进

 1 year ago
source link: https://blog.51cto.com/u_13556371/5868401
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.

Restful API 设计最佳实践已经被讨论过多次,其中命名规则有共识也有差异。从函数实现的角度出发,基于简单、明确的原则,在考虑对接 RPC API 的情况下,可以发现一些冲突和理解上的难点,本文做出了四项改进。

1 资源名称单复数问题

资源名称使用单数或者复数本是习惯约定,只要表意明确即可。本文强烈推荐使用单数,理由如下。

(1)使用单数顺应中文常识

虽然指人时有“我”、“我们”、“你”、“你们”,区分单复数。但指物或者类时,比如“一批书”和“一本书”,都是用“书”,不使用“书们”,没有区分单复数。其他示例如,“一篇文章”、“全部文章”,一般不使用“文章们”,“一位工程师”、“多位工程师”,一般不使用“工程师们”。

(2)单复数转换引起麻烦

英文的单复数转换规则复杂,有些单词单复数相同,这增加了理解的难度,降低了准确度。

(3)缩写没有复数

有些资源就是资源分类名称,甚至是缩写,使用复数表示没有意义,反而容易引发歧义。

(4)保持业务术语一致性

使用资源的地方较多,比如资源 URL、结构或者对象名称、数据库表。结构或者对象名称使用单数的习惯非常固定,资源 URL 和数据库表名等使用单数非常简单,可以避免在整体业务中资源名称转换的额外开销和理解成本。

当然对于 mongodb 等文档数据库,数据库表名习惯就是使用复数,那就忽略吧。

(5)简单原则优先

随着零代码平台推出,开发 API 的门槛降低,人员范围变大,单复数转换极有可能就是一个知识痛点。

2 资源分类和 URL 映射

资源的操作方式有很多种,函数的实现是确定的,这就存在了一对多的可能性。

(1)资源的同类操作对应于同一 URL 和 HTTP 操作引发冲突

例如创建图书信息,既可以一次创建一本图书的信息,也可以一次创建一批图书的信息。如果只使用资源名称和 HTTP 谓词,那么结果就是 ​​POST /v1/api/book​​,表示创建一批图书信息的 API,似乎可以解决问题。

但如果对 API 增加约束,比如普通用户使用一次创建一本图书信息的 API,小组管理员使用一次创建一批图书信息的 API,那么就必须开发两个 API 分别配置。显然一个答案并不能满足两个 API 的需求,以往的最佳实践并没有对此类问题给出答案。

(2)函数单一职责要求多个 URL 映射

就此,我们将图书信息创建函数展开,分为四个来讨论如何制定资源 URL 和操作。(1)一次创建一本书的信息,(2)一次创建一批书的信息,(3)使用电子表格文件批量创建一批书的信息,(4)从指定网上的电子书单创建一批书的信息。

对于 HTTP API 来说,当然可以在请求中定义多个参数,既可以又可以,以此处理四件事情。但是对于函数来说,肯定要分类讨论,一次只做一件事情。在精细化管理的情况下,如果一个 HTTP API 对应一个函数最好。

如果可以使用 Header 参数,当然可以。但大多数 http 框架并没有默认支持映射,需要自己处理,从语义理解上落了下风。

(3)关于 POST 的单个资源实例和资源集合操作

网上最佳实践对 POST 的 URL 规则有些奇怪,因为 POST 和 其他谓词的规则相反,具体如下。

【API设计与开发实践】第2篇 Restful API 设计最佳实践的四个重要改进_Restful

显然操作单个资源实例和操作资源集合并没有进行有效区分。

如果是预先知道资源的ID,那么可以使用 ​​POST /v1/api/company-1​​​ 或者 ​​​POST /v1/api/company/1​​​,如果不知道呢,是使用 ​​POST /v1/api/company-​​​ 或者 ​​POST /v1/api/company/​​ 吗?显然这些容易造成理解困难。

网上最佳实践并没有给出这个问题的答案,就需要自己实践。

(4)合理对资源分类,结合下级资源明确 URL

将资源来源进行分类,使用上下级资源嵌套,可以明确 URL 如下。

  • 一次创建一本书的信息,使用 ​​POST /v1/api/book​
  • 一次创建一批书的信息,使用 ​​POST /v1/api/book/list​
  • 使用电子表格文件批量创建一批书的信息,使用 ​​POST /v1/api/book/file​
  • 从指定网上的电子书单创建一批书的信息,使用 ​​POST /v1/api/book/ticket​

这里,对于 POST 操作,依照已有习惯,为了兼容其他分类,依旧使用反例。

3 资源ID

读取资源信息时,网上很多实践使用分隔符单独表示资源ID。

例如 ​​GET /v1/api/book/1​​​ 表示读取ID=1的书,​​GET /v1/api/book/1/comment/10​​​ 表示读取 ID=1 的书的 commentID = 10 的评论。

但由于资源分类原则,可能存在 ​​GET /v1/api/book/file​​​ ,表示图书信息的电子表格文件。这里就存在 ​​1​​​ 和 ​​file​​ 的路由冲突,理解上增加了难度,技术处理了增加了难度。

将资源 ID 和资源使用中横线连接表示更加简洁,易于理解。

如将 ​​GET /v1/api/book/1​​​ 表示为 ​​GET /v1/api/book-1​​​,将 ​​GET /v1/api/book/1/comment/10​​​ 表示为 ​​GET /v1/api/book-1/comment-10​​。

【API设计与开发实践】第2篇 Restful API 设计最佳实践的四个重要改进_API设计_02

综上,URL的范式是

/VERSION/api/[RESOURCE|RESOURCE-INSTANCE]/[SUB-RESOURCE|SUB-RESOURCE-INSTANCE]

4 HTTP API 和函数名称映射

这在历次实践中并没有涉及到,出于简化的考虑有需求,列出如下。

VERB & URL 

函数包名/名称

常用方法名

​POST /v1/api/book​

v1.book.post.one

create

​POST /v1/api/book/list​

v1.book.post.list

create

​POST /v1/api/book/file​

v1.book.post.file

create

​POST /v1/api/book/ticket​

v1.book.post.ticket

create

​DELETE /v1/api/book-1​

v1.book.del.one

delete

​DELETE /v1/api/book​

v1.book.del.list

delete

​PUT /v1/api/book-1​

v1.book.put.one

update

​PUT /v1/api/book​

v1.book.put.list

update

​PATCH /v1/api/book-1​

v1.book.patch.one

update

​PATCH /v1/api/book​

v1.book.patch.list

update

​GET /v1/api/book​

v1.book.get.one

​GET /v1/api/book-1​

v1.book.get.list

​POST /v1/api/book-1/comment​

v1.book.comment.post.one

create

​DELETE /v1/api/book-1/comment-1​

v1.book.comment.del.one

delete

​DELETE /v1/api/book-1/comment​

v1.book.comment.del.list

delete

​PUT /v1/api/book-1/comment-1​

v1.book.comment.put.one

update

​PUT /v1/api/book-1/comment​

v1.book.comment.put.list

update

​GET /v1/api/book-1/comment-1​

v1.book.comment.get.one

​GET /v1/api/book-1/comment​

v1.book.comment.get.list

表中方法根据需要扩展,主要特点集中在对应函数名了,使用了 http 谓词,使用 one/list 限定词。

这里使用 del 替代 delete,因为一些语言中 delete 是关键字。

one 和 list 是区分对单个/多个对象或者记录的操作。

列出也时考虑 RPC API 的需要,当然 RPC API 的名称还需要考虑 stream 的需要,例如读取流数据时可能使用 v1.book.get.stream,与 v1.book.get.list 进行区分。

这样就实现了 HTTP API 、RPC API 和函数全名的全映射。

API 的改进都是本着简洁、明确的原则,从发展的角度出发。本文对同类操作进行了资源分类,避免冲突,尝试规范了 HTTP API 和 RPC API 函数命名规则。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK