Qua*_*kly 6 rest api-design http bulk optimistic-concurrency
我有一个中间层,它在共享数据库上执行CRUD操作.当我将产品转换为.NET Core时,我认为我也会考虑使用REST作为API,因为CRUD应该是它的功能.似乎REST对于单个记录操作来说是一个很好的解决方案,但是当我想要删除1000条记录时会发生什么?
每个专业的多用户应用程序都会有一些乐观并发检查的概念:如果没有一些反馈,你就不能让一个用户消除另一个用户的工作.据我了解,REST使用HTTP ETag头记录处理此问题.如果客户端发送的ETag与服务器的标记不匹配,则发出412 Precondition Failed.到现在为止还挺好.但是,当我想删除1,000条记录时,我会使用什么?1,000次单独调用的来回时间是相当可观的,那么REST如何处理涉及Optimistic Concurrency的批处理操作?
如果我理解正确的话,您希望每个记录都单独实现乐观并发。也就是说,只有当每条记录的状态符合 client\xe2\x80\x99s 的期望时,才会删除该记录。(如果您只想断言整个集合\xe2\x80\x99s 状态,则If-Match和 412 就足够了。)
Roman Vottner\xe2\x80\x99s 的答案很好地解释了所涉及的 HTTP 方法,但我\xe2\x80\x99 将尝试填写一些细节。
\n当我们谈论 \xe2\x80\x9chow 将 REST 处理 \xe2\x80\x9d 这样或那样时,您就会明白,从技术上讲,您可以使用 HTTP 作为任何适合您的操作的传输。
\n因此,当您\xe2\x80\x99询问REST时,我\xe2\x80\x99m假设您\xe2\x80\x99对统一接口感兴趣\xe2\x80\x94一种理论上可以被一系列使用的方法各种客户端和服务器。
\n但那里的关键词是\xe2\x80\x9c理论上\xe2\x80\x9d。例如,一旦您定义了自己的媒体类型(您自己的 JSON 结构),很多一致性都会付之东流,因为客户端无论如何都必须根据您的特定 API 进行编码,此时您可以要求它跳转通过任何你想要的圈。
\n但如果您\xe2\x80\x99 仍然对尽可能多地挽救一致性感兴趣,那么请继续阅读。
\n如果您想要一个全有或全无的操作,如果任何一个单独的先决条件失败,该操作就会完全失败,那么,正如 Roman 建议的那样,您可以使用带有JSON Patch格式的PATCH。为此,您需要将集合概念性地表示为要应用补丁的单个 JSON 对象。
\n例如,假设您有/my/collection/1、/my/collection/4、 等资源。您可以表示/my/collection/为:
{\n "resources": {\n "1": {\n "href": "1",\n "etag": "\\"BRkDVtYw\\"",\n "name": "Foo Bar",\n "price": 1234.5,\n ...\n },\n "4": {\n "href": "4",\n "etag": "\\"RCi8knuN\\"",\n "name": "Baz Qux",\n "price": 2345.6,\n ...\n },\n ...\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n这里,"1"和"4"是相对于/my/collection/。您可以使用特定于域的 ID 来代替,但正确的 REST 是根据不透明的 URL 进行操作的。
标准 don\xe2\x80\x99t 要求您实际在GET /my/collection/,但如果您确实支持此类请求,那么您应该使用该表示。无论如何,您可以对此结构应用以下 JSON 补丁:
PATCH /my/collection/ HTTP/1.1\nContent-Type: application/json-patch+json\n\n[\n {"op": "test", "path": "/resources/1/etag", "value": "\\"BRkDVtYw\\""},\n {"op": "remove", "path": "/resources/1"},\n {"op": "test", "path": "/resources/4/etag", "value": "\\"RCi8knuN\\""},\n {"op": "remove", "path": "/resources/4"},\n ...\n]\nRun Code Online (Sandbox Code Playgroud)\n这里,path不是一个URL路径,它\xe2\x80\x99是一个JSON指针指向上述表示的
如果所有补丁操作均成功,则您将使用成功的状态代码进行响应,例如204(无内容)或200(正常)。
\n如果任何 ETagtest操作失败,您将响应409(冲突)。在这种情况下,您不应响应412(前提条件失败)If-Match ,因为请求本身没有前提条件(如)。
如果出现其他问题,您可以使用其他适当的状态代码进行响应:请参阅RFC 5789 \xc2\xa7 2.2和RFC 7231 \xc2\xa7 6.6。
\n如果您不想要 \xe2\x80\x99call-or-nothing\xe2\x80\x9d 语义,那么我 \xe2\x80\x99m 不知道任何标准化解决方案。正如 Roman 所指出的,在这种情况下您不能使用 PATCH 方法,但您可以将 POST 与自定义媒体类型 ( RFC 6838 \xc2\xa7 3.4 ) 结合使用。它可能看起来像这样:
\nPOST /my/collection/ HTTP/1.1\nContent-Type: application/x.my-patch+json\nAccept: application/x.my-patch-results+json\n\n{\n "delete": [\n {"href": "1", "if-match": "\\"BRkDVtYw\\""},\n {"href": "4", "if-match": "\\"RCi8knuN\\""},\n ...\n ]\n}\nRun Code Online (Sandbox Code Playgroud)\n无论单个删除是否成功,您都可以使用 200(确定)来响应此类请求。另一个选择是207 (Multi-Status),但我在这种情况下看不到它有任何好处,并且它在 WebDAV 之外没有广泛使用,所以Postelxe2x80 \x99的法律建议不要去那里。
\nHTTP/1.1 200 OK\nContent-Type: application/x.my-patch-results+json\n\n{\n "delete": [\n {"href": "1", "success": true},\n {"href": "4", "success": false, "error": {...}},\n ...\n ]\n}\nRun Code Online (Sandbox Code Playgroud)\n当然,如果补丁一开始就无效,您应该根据情况响应415(不支持的媒体类型)或422(无法处理的实体)。
\n\n\n1000个单独的通话来回时间相当可观
\n
它位于 HTTP/1.1 中。但是,如果您可以使用 HTTP/2 \xe2\x80\x94,它对并发请求有更好的支持,并且每个请求 \xe2\x80\x94 的网络开销要小得多,那么 1000 个单独的请求可能适合您。
\nREST 的重点是资源和客户端与服务器的解耦,尽管它不是简单的 CRUD 架构或协议。虽然 CRUD 和 REST 似乎非常相似,但通过 REST 原则管理资源通常也有副作用。因此,将 REST 描述为简单的 CRUD 是过于简单化了。
关于 REST 资源的批处理,底层协议(最常见的是 HTTP)确实定义了可以使用的功能。HTTP 定义了几个可用于修改多个资源的操作。
POST是协议的万能瑞士军刀,可用于根据自己的喜好管理资源。由于语义是由开发人员定义的,您可以使用它一次创建、更新或删除多个资源。
PUT具有将在给定 URI 处可获取的资源状态替换为请求的有效负载正文的语义。如果您向PUT“列表”资源发送请求并且负载定义了条目列表,您也可以实现批量操作。
POST 和 PUT 方法之间的根本区别通过封闭表示的不同意图突出显示。POST 请求中的目标资源旨在根据资源自己的语义处理封闭的表示,而 PUT 请求中的封闭表示被定义为替换目标资源的状态。
...
应用于目标资源的 PUT 请求可能会对其他资源产生副作用。例如,一篇文章可能有一个用于标识“当前版本”(资源)的 URI,该 URI 与标识每个特定版本的 URI(不同的资源,在某一时刻与当前版本资源共享相同的状态)是分开的。因此,对“当前版本”URI 的成功 PUT 请求除了会更改目标资源的状态之外,还可能创建新版本资源,并且还可能导致在相关资源之间添加链接。(来源)
PATCH( RFC 5789 ) 尚未包含在 HTTP 协议中,尽管有很多框架支持。主要用于一次更改多个资源或对资源进行部分更新,PUT如果更新的部分是其他资源的子资源,也可以实现;在这种情况下,它会对外部资源产生部分更新的影响。
重要的是要知道PATCH请求包含服务器必须完成的将资源转换为其预期状态的必要步骤。因此,客户端必须获取当前状态并预先计算转换所需的必要步骤。关于此主题的一篇内容丰富的博文是Don't patch like an idiot。这里的JSON Patch ( RFC ) 是一种基于 JSON 的媒体类型,它清楚地可视化了 PATCH 概念。补丁请求必须完全应用(补丁请求中定义的每个操作)或根本不应用。因此,它需要一个事务范围的处理和回滚,以防任何操作失败。
条件请求ETag和IfModifiedSince标头在RFC 7232中定义,仅当请求应用于最新版本的资源并因此与(分布式)数据库中的乐观锁定相关联时,才可以在 HTTP 请求中使用以执行修改。
到现在为止还挺好。但是当我想删除 1,000 条记录时我用什么?
这取决于您将使用什么框架。如果它支持PATCH我明确投票给PATCH. 如果没有,你可能使用更安全POST比PUT作为非常严格的语义PUT有,因为这个语义明确由您定义即可。在批量删除的情况下,PUT也可以通过以空体为目标的集合资源来使用,其结果是删除集合中的任何项目并因此清除整个集合。如果某些项目应该保留在集合中,PATCH或者POST可能更易于使用。
| 归档时间: |
|
| 查看次数: |
8318 次 |
| 最近记录: |