一个重大的误会是 RESTAPI 必须是基于 CRUD 的,这两者之间没有任何的分割,都只是API设计格调的一种形式而已。本文还将介绍基于 REST 的 API 的几种实现规定。

CRUD简介

基于 CRUD 的 API 是指提供蕴含实例的资源汇合的 API,效仿 create、read、update 和 delete 生命周期模式。当有一组代表内容或状态的资源实例时,CRUD 模式很有用,它通常遵循以下模式:

  • GET /articles – 列出/分页/过滤文章列表
  • POST /articles – 创立新文章
  • GET /articles/{articleId} – 获取文章详情
  • PATCH /articles/{articleId} – 更新文章详情
  • DELETE /articles/{articleId} – 删除指定ID的文章

REST:超过 CRUD

在探讨 CRUD 之外的API格调之前,须要廓清一个对于 REST 的重大误会:

CRUD 并不是对“RESTful”API 的要求。

事实上,在 Fielding’s dissertation 的论文中并没有提及 CRUD

在谈到基于 REST 的 API 时,很多人将利用基于 CRUD 格调的资源与 REST 格调混同一起。然而,这些其实是 HTTP 形象的资源概念,与数据库设计没有间接关系:

本标准没有限度可能是资源,相同“资源”一词是在个别意义上应用的用于任何可能被 URI 标识的内容。—— RFC 3986

REST 是标准客户端和服务端之间通信的形式,利用 HTTP 协定提供的性能,这些限度能够自在地专一于 API 设计:

  1. 对立的接口:来自不同客户端的申请都是一样的,无论客户端是浏览器、挪动设施还是其余任何设施
  2. 客户端-服务端拆散 :客户端和服务端独立运行,它们之间的交互仅以申请和响应的模式进行
  3. 无状态 :服务端不存储应用 API 的用户的任何信息,因而客户端必须在每次申请时提供解决申请的所有必要信息
  4. 分层零碎:客户端不晓得在客户端和响应申请的理论服务器之间有多少层,这是 HTTP 的一个要害准则,容许缓存服务器、反向代理和拜访平安分层——所有这些对发送申请的客户端都是通明的
  5. 可缓存 : 服务端响应必须蕴含无关数据是否可缓存的信息,容许客户端或代理在 API 服务器之外缓存数据
  6. 按需代码(可选):可能依据申请将可执行代码从服务器发送到客户端,从而扩大客户端性能。

同样,REST 不须要具备基于 CRUD 格调的资源。CRUD 是一种咱们能够利用于 REST 的 API 格调,但它不是形成基于 REST API 的必要条件。

这在设计 API 时能够更加的自在,这样能够在适当的时候提供具备基于 CRUD 格调的资源,而在不须要CRUD的时候混合应用功能性资源。

在设计 API 时,不用局限于这种格调,能够摸索一些其余扩大格调,能够帮忙设计出满足以后和将来开发需要的杰出的API格调。

应用端点扩大 CRUD

API 设计中的一个常见状况是须要超过典型的 CRUD 交互模型,例如,某些 API 可能须要反对提交、 批准、回绝和公布等操作。因为在 HTTP 中的办法无限,如何将其增加到的 API 中?

一种抉择是应用通过 PATCH 申请批改的状态字段,尽管这是一种抉择,但感觉应用性能端点可能更加清晰,如下:

  • PUT /articles/{articleId}/submit
  • PUT /articles/{articleId}/approve
  • PUT /articles/{articleId}/decline
  • PUT /articles/{articleId}/publish

在此设计中,为汇合中的文章实例提供了性能端点,通过 PUT 操作应用(意味着只是更新),这样设计有以下益处:

  1. 在 API 管理层能够有更好的细粒度拜访治理,每个特定的操作都是一个惟一的 URL,能够调配不同的基于角色(RBAC)的拜访。 因而,将拜访权限从代码中解耦到部署模型中,能够灵便地进一步限度端点拜访,而无需更改代码来强制执行这些限度
  2. API 反对的工作流更加明确,不用查看PATCH端点来查看能够更新的无效状态值,以及容许转换到的状态和工夫的状态机规定。每个端点都独立且更直观地申明了这一点
  3. 最初,通过为额定的流程端点提供惟一的 URL ,可能在响应负载中更无效地利用超媒体链接。例如,作者可能会看到以下 API 响应:

    超媒体链接:API不仅仅是HTTP上的数据存储,而是成为HTTP上的状态机。它依然有数据,然而它也能够以自我形容的形式提供"下一个可用动作"
{    name: "布道api",    status: "draft",    _links: [{ rel: "submit", href: "/articles/99/submit" }],}

一旦文章草稿提交,编辑的时候可能会看到以下内容:

{    name: "api布道",    status: "draft",    _links: [        { rel: "approve", href: "/articles/99/approve" },        { rel: "decline", href: "/articles/99/decline" },    ],}

客户端可能依据用户的权限理解客户以后能够应用哪些操作(例如,编辑可能可能批准、回绝和公布,而作者可能只能以草稿模式提交文章) 通过是否存在指向“下一个可用动作”的端点链接。

对于须要超过其提供的局部或全副资源的规范创立、读取、更新和删除操作的 API 设计来说,这是一个十分实用的选项。

用于搜寻和计算的性能资源

如果须要为消费者提供的能力不须要资源汇合怎么办?如果只须要计算一个值并返回它呢?这会毁坏基于 REST 的办法吗?不须要,只有咱们恪守 HTTP 规定并依据须要抉择正确的动作动词,通常是 POSTGET

一些常见的例子可能包含:

  • GET /search?q=devpoint
  • GET /verification-code?code=202106
  • POST /sales-tax

通常性能资源的命名模式是动词-名词的模式,有时只是动词的模式。这使得开发者很容易了解它是一个性能资源,而不是一个遵循复数名词格局的资源汇合(例如我的项目、 账户、 客户)。

用于简单、长时间运行的工作流的事务资源

在 SOAP 和 Web 服务时代,WS-Transaction 是一种标准,目标是将多个 Web 服务调用封装到单个分布式事务中,这将增大服务设计的复杂度。

应用基于 REST 的API设计,同样会遇到简单的 API 设计,须要跨多个 HTTP 申请的状况。这里介绍一种易实现且灵便的解决方案。

资源的设计不仅能够示意业务对象,还能够用于示意长时间运行的事务或简单的工作流。这些资源汇合示意须要为API使用者提供的长时间运行的事务或工作流。上面来看一个酒店管理系统的预约流程:

  1. 首先提交一个申请来创立一个新的预订资源实例,其中蕴含要预约的房间详细信息,例如: POST /reservations
  2. 而后,应用这个资源实例来获取或批改预订的详细信息,包含入店和离店日期、预约人数、额定申请等。这个端点可能是 PATCH /reservations/hotel66,将依据须要常常应用它,直到预订实现了客户的所需要求
  3. 为了减少灵活性和审核的目标,每次进行批改时,都能够将更改获取为一个独自的嵌套资源,容许提取预订更改的历史记录,例如 GET /reservations/hotel66/modifications
  4. 如果有必要,能够为预订设定一个过期日期,如创立之后的1天。如果试图在过期后进行更新或领取预订费用,将收到一个 400 Bad Request409 Conflict
  5. 筹备好后,客户能够为预订订单进行付款,例如 POST /reservations/hotel66/payments
  6. 胜利领取的后果会就意味着预订实现,须要获取相应信息,例如 GET /bookings/book66
  7. 预订胜利之后,就须要更新订单的解决状态,例如 PATCH /bookings/book66,如须要额定领取费用或局部或全副退款。

这是一种比拟正当的设计,能够对长时间运行的事务或工作流流程建模,对资源的更改放弃审核跟踪,并反对事务的过期工夫。同时放弃在基于 REST 的API设计的束缚范畴内,防止减少API使用者的复杂性。

单例资源:只能有一个

当 API 不须要资源汇合时,单例资源很有用。单例资源能够代表一个虚构资源,用于与现有资源实例间接交互,例如 GET /me 代替 GET /users/12345

当父资源和子资源之间存在且只有一种关系时,API 能够提供嵌套的单例资源,例如 PUT /accounts/66/preferences

单例资源可能曾经存在而无需创立它们,或者它们可能须要通过 POST 端点创立。只管单例资源可能无奈像基于汇合的同类资源那样提供残缺的 CRUD 格调的所有操作,但它们仍应保持正确抉择最适宜必要操作的 HTTP 动作动词。

批量和批量资源操作

有些资源须要一次性批量导入大量数据,以避免出现提交10010000次独自的POST操作,这时就须要 bulkbatch 解决。尽管这两个术语有时能够调换应用,但感觉应该进行辨别:

  • bulk 批量操作在解决每个提交的记录,容许呈现解决失败的,但导入的其余部分是胜利,意味着在1000项记录中,可能有76条失败了,而余下的924项记录胜利导入。客户端须要获取未能胜利导入的记录信息以便批改再提交未能胜利的记录。
  • batch 批处理操作在单个通过或失败事务中解决所有提交的记录,意味着1000项记录中如果有76条记录失败,那么理论存储的记录就没有一条。客户须要批改76条不合格记录,再从新提交批改后的记录。

总结

当谈到基于REST的API时,很多人将利用基于 CRUD 模式的资源与REST一概而论。REST是一组束缚(标准),以便在前后端开发中合作中更加高效。对于API设计,集体倡议还是须要花点工夫设计,抉择一个最佳的模式。