REST API - 在单个请求中批量创建或更新 [英] REST API - Bulk Create or Update in single request
问题描述
假设有两个资源 Binder
和 Doc
具有关联关系意味着 Doc
和 Binder
代表他们自己.Doc
可能属于也可能不属于 Binder
并且 Binder
可能为空.
如果我想设计一个 REST API,允许用户发送 Doc
的集合,在单个请求中,如下所示:
<代码>{文档":[{"doc_number": 1, "binder": 1},{"doc_number": 5, "binder": 8},{"doc_number": 6, "binder": 3}]}
对于 docs
中的每个文档,
- 如果
doc
存在,则将其分配给Binder
- 如果
doc
不存在,创建它然后分配它
我真的很困惑应该如何实施:
- 使用什么 HTTP 方法?
- 必须返回什么响应代码?
- 这是否符合 REST 的要求?
- URI 会是什么样子?
/binders/docs
? - 处理批量请求,如果有几个项目引发错误但另一个通过了怎么办.必须返回什么响应代码?批量操作应该是原子操作吗?
我认为您可以使用 POST 或 PATCH 方法来处理此问题,因为它们通常为此而设计.
使用
POST
方法 通常用于在列表资源上添加元素,但您也可以支持此方法的多种操作.请参阅此答案:以 REST 方式更新整个资源集合.您还可以支持输入的不同表示格式(如果它们对应于数组或单个元素).在这种情况下,没有必要定义您的格式来描述更新.
使用
<块引用>PATCH
方法也是合适的,因为相应的请求对应于部分更新.根据 RFC5789 (https://www.rfc-editor.org/rfc/rfc5789):一些扩展超文本传输协议 (HTTP) 的应用程序需要一个功能来进行部分资源修改.现有的 HTTP PUT 方法只允许完全替换文档.该提案添加了一个新的 HTTP 方法 PATCH,用于修改现有的 HTTP 资源.
在这种情况下,您必须定义格式来描述部分更新.
我认为在这种情况下,POST
和 PATCH
非常相似,因为您实际上不需要描述对每个元素执行的操作.我会说这取决于要发送的表示的格式.
PUT
的情况有点不太清楚.实际上,当使用方法PUT
时,您应该提供整个列表.事实上,请求中提供的表示将替换列表资源一.
关于资源路径,您可以有两个选项.
- 使用文档列表的资源路径
在这种情况下,您需要在请求中提供的表示中明确提供带有活页夹的文档链接.
这是此 /docs
的示例路线.
这种方法的内容可以用于方法POST
:
- 使用活页夹元素的子资源路径
此外,您还可以考虑利用子路由来描述文档和绑定器之间的链接.现在不必在请求内容中指定有关文档和活页夹之间关联的提示.
这是此 /binder/{binderId}/docs
的示例路线.在这种情况下,使用 POST
或 PATCH
方法发送文档列表将在创建文档后将文档附加到具有标识符 binderId
的活页夹如果它不存在.
这种方法的内容可以用于方法POST
:
关于响应,您可以定义响应级别和要返回的错误.我看到两个级别:状态级别(全局级别)和有效负载级别(更薄级别).您还可以定义与您的请求相对应的所有插入/更新是否必须是原子的.
- 原子
在这种情况下,您可以利用 HTTP 状态.如果一切顺利,您将获得状态 200
.如果不是,如果提供的数据不正确(例如活页夹 ID 无效)或其他内容,则出现另一个状态,例如 400
.
- 非原子
在这种情况下,将返回状态 200
并且由响应表示来描述已完成的操作以及最终发生错误的位置.ElasticSearch 在其 REST API 中有一个用于批量更新的端点.这可以给你一些这个级别的想法:http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html.
- 异步
您还可以实现异步处理来处理提供的数据.在这种情况下,HTTP 状态返回将是 202
.客户端需要拉一个额外的资源来看看会发生什么.
在完成之前,我还想注意到 OData 规范解决了具有名为导航链接的功能的实体之间的关系问题.也许你可以看看这个 ;-)
以下链接也可以帮助您:https://templth.wordpress.com/2014/12/15/designing-a-web-api/.
希望对你有帮助蒂埃里
Let's assume there are two resources Binder
and Doc
with association relationship meaning that the Doc
and Binder
stand on their own. Doc
might or might not belong to Binder
and Binder
might be empty.
If I want to design a REST API that allows a user to send a collection of Doc
s, IN A SINGLE REQUEST, like the following:
{
"docs": [
{"doc_number": 1, "binder": 1},
{"doc_number": 5, "binder": 8},
{"doc_number": 6, "binder": 3}
]
}
And for each doc in the docs
,
- If the
doc
exists then assign it toBinder
- If the
doc
doesn't exist, create it and then assign it
I'm really confused as to how this should be implemented:
- What HTTP method to use?
- What response code must be returned?
- Is this even qualified for REST?
- How would the URI look like?
/binders/docs
? - Handling bulk request, what if a few items raise an error but the other go through. What response code must be returned? Should the bulk operation be atomic?
I think that you could use a POST or PATCH method to handle this since they typically design for this.
Using a
POST
method is typically used to add an element when used on list resource but you can also support several actions for this method. See this answer: Update an entire resource collection in a REST way. You can also support different representation formats for the input (if they correspond to an array or a single elements).In the case, it's not necessary to define your format to describe the update.
Using a
PATCH
method is also suitable since corresponding requests correspond to a partial update. According to RFC5789 (https://www.rfc-editor.org/rfc/rfc5789):Several applications extending the Hypertext Transfer Protocol (HTTP) require a feature to do partial resource modification. The existing HTTP PUT method only allows a complete replacement of a document. This proposal adds a new HTTP method, PATCH, to modify an existing HTTP resource.
In the case, you have to define your format to describe the partial update.
I think that in this case, POST
and PATCH
are quite similar since you don't really need to describe the operation to do for each element. I would say that it depends on the format of the representation to send.
The case of PUT
is a bit less clear. In fact, when using a method PUT
, you should provide the whole list. As a matter of fact, the provided representation in the request will be in replacement of the list resource one.
You can have two options regarding the resource paths.
- Using the resource path for doc list
In this case, you need to explicitely provide the link of docs with a binder in the representation you provide in the request.
Here is a sample route for this /docs
.
The content of such approach could be for method POST
:
[
{ "doc_number": 1, "binder": 4, (other fields in the case of creation) },
{ "doc_number": 2, "binder": 4, (other fields in the case of creation) },
{ "doc_number": 3, "binder": 5, (other fields in the case of creation) },
(...)
]
- Using sub resource path of binder element
In addition you could also consider to leverage sub routes to describe the link between docs and binders. The hints regarding the association between a doc and a binder doesn't have now to be specified within the request content.
Here is a sample route for this /binder/{binderId}/docs
. In this case, sending a list of docs with a method POST
or PATCH
will attach docs to the binder with identifier binderId
after having created the doc if it doesn't exist.
The content of such approach could be for method POST
:
[
{ "doc_number": 1, (other fields in the case of creation) },
{ "doc_number": 2, (other fields in the case of creation) },
{ "doc_number": 3, (other fields in the case of creation) },
(...)
]
Regarding the response, it's up to you to define the level of response and the errors to return. I see two levels: the status level (global level) and the payload level (thinner level). It's also up to you to define if all the inserts / updates corresponding to your request must be atomic or not.
- Atomic
In this case, you can leverage the HTTP status. If everything goes well, you get a status 200
. If not, another status like 400
if the provided data aren't correct (for example binder id not valid) or something else.
- Non atomic
In this case, a status 200
will be returned and it's up to the response representation to describe what was done and where errors eventually occur. ElasticSearch has an endpoint in its REST API for bulk update. This could give you some ideas at this level: http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html.
- Asynchronous
You can also implement an asynchronous processing to handle the provided data. In this case, the HTTP status returns will be 202
. The client needs to pull an additional resource to see what happens.
Before finishing, I also would want to notice that the OData specification addresses the issue regarding relations between entities with the feature named navigation links. Perhaps could you have a look at this ;-)
The following link can also help you: https://templth.wordpress.com/2014/12/15/designing-a-web-api/.
Hope it helps you, Thierry
这篇关于REST API - 在单个请求中批量创建或更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!