REST API – 在单个请求中批量创build或更新
假设有两个资源“ Binder
和Doc
关联关系”,这意味着“ Doc
和“ Binder
独立的。 Doc
可能或可能不属于Binder
而Binder
可能是空的。
如果我想devise一个REST API,允许用户发送一个Doc
集合, 在一个单一的请求 ,如下所示:
{ "docs": [ {"doc_number": 1, "binder": 1}, {"doc_number": 5, "binder": 8}, {"doc_number": 6, "binder": 3} ] }
对于文档中的每个docs
,
- 如果
doc
存在,则将其分配给Binder
- 如果
doc
不存在,请创build它,然后分配它
我真的很困惑这应该如何实施。
- 使用什么HTTP方法?
- 必须返回哪些响应代码?
- 这甚至是合格的REST?
- URI将如何?
/binders/docs
? - 处理批量请求,如果有几个项目出现错误,但另一个项目则通过。 必须返回哪些响应代码? 批量操作应该是primefaces的吗?
我认为你可以使用POST或PATCH方法来处理这个问题,因为它们通常是为此而devise的。
-
使用
POST
方法通常用于在列表资源上添加元素,但您也可以支持该方法的多个操作。 看到这个答案: 如何更新一个REST资源集合 。 你也可以为input支持不同的表示格式(如果它们对应一个数组或单个元素的话)。在这种情况下,没有必要定义格式来描述更新。
-
使用
PATCH
方法也是合适的,因为相应的请求对应于部分更新。 根据RFC5789( http://tools.ietf.org/html/rfc5789 ):扩展超文本传输协议(HTTP)的几个应用程序需要function来进行部分资源修改。 现有的HTTP PUT方法只允许完全replace文档。 该build议添加了一个新的HTTP方法PATCH来修改现有的HTTP资源。
在这种情况下,您必须定义您的格式来描述部分更新。
我认为在这种情况下, POST
和PATCH
非常相似,因为您并不需要描述每个元素的操作。 我想说这取决于发送的表示的格式。
PUT
的情况有点不太清楚。 实际上,在使用PUT
方法时,应该提供整个列表。 事实上,请求中提供的表示将替代列表资源之一。
您可以有两个关于资源path的选项。
- 使用doc列表的资源path
在这种情况下,您需要在请求中提供的表示中明确提供文档的链接。
这是一个这个/docs
的示例路线。
这种方法的内容可能是方法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) }, (...) ]
- 使用binder元素的子资源path
此外,您还可以考虑利用子路线来描述文档和活页夹之间的链接。 关于文档和活页夹之间关联的提示现在不需要在请求内容中指定。
以下是此/binder/{binderId}/docs
的示例路由。 在这种情况下,使用POST
或PATCH
方法发送文档列表将在创build文档(如果不存在)后将文档附加到具有标识符binderId
的活页夹。
这种方法的内容可能是方法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) }, (...) ]
关于答复,您需要定义答复的等级和要返回的错误。 我看到两个级别:状态级别(全局级别)和有效负载级别(更薄级别)。 您也可以自己定义是否所有与您的请求相对应的插入/更新都必须是primefaces的。
- primefaces
在这种情况下,您可以利用HTTP状态。 如果一切顺利,你的状态是200
。 如果不是,如果提供的数据不正确(例如binder id无效)或其他的其他状态,如400
。
- 非primefaces的
在这种情况下,状态200
将被返回,并且由响应表示来描述所做的事情以及错误最终发生的位置。 ElasticSearch在其REST API中有一个用于批量更新的端点。 这可以给你一些这个级别的想法: http : //www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html 。
- asynchronous
你也可以实现一个asynchronous处理来处理提供的数据。 在这种情况下,HTTP状态返回将是202
。 客户端需要额外的资源来看看会发生什么。
在完成之前,我也想注意到,OData规范解决了与具有导航链接function的实体之间的关系问题。 也许你可以看看这个;-)
以下链接也可以帮助您: https : //templth.wordpress.com/2014/12/15/designing-a-web-api/ 。
希望它能帮助你,Thierry
您可能需要使用POST或PATCH,因为更新和创build多个资源的单个请求不太可能是幂等的。
做PATCH /docs
绝对是一个有效的select。 你可能会发现使用标准的补丁格式对你的特定场景来说很棘手。 不确定这一点。
你可以使用200.你也可以使用207 – 多状态
这可以通过RESTful方式完成。 在我看来,关键是要有一些资源来接受一组文件来更新/创build。
如果你使用PATCH方法,我认为你的操作应该是primefaces的。 即,我不会使用207状态码,然后在响应主体中报告成功和失败。 如果你使用POST操作,那么207方法是可行的。 您将不得不devise自己的响应主体来通信哪些操作成功,哪些失败。 我不知道一个标准化的。
PUT ing
PUT /binders/{id}/docs
创build或更新,并将单个文档关联到联编程序
例如:
PUT /binders/1/docs HTTP/1.1 { "docNumber" : 1 }
修补
PATCH /docs
如果文档不存在并将其与粘合剂关联,则创build文档
例如:
PATCH /docs HTTP/1.1 [ { "op" : "add", "path" : "/binder/1/docs", "value" : { "doc_number" : 1 } }, { "op" : "add", "path" : "/binder/8/docs", "value" : { "doc_number" : 8 } }, { "op" : "add", "path" : "/binder/3/docs", "value" : { "doc_number" : 6 } } ]
稍后我会提供更多的见解,但同时如果您愿意,请参阅RFC 5789 , RFC 6902和William Durand的Please。 不要像一个白痴博客条目补丁 。
在我工作的一个项目中,我们通过实施一些我们称之为“批处理”的请求来解决这个问题。 我们按以下格式定义了一个path/batch
,我们接受了json:
[ { path: '/docs', method: 'post', body: { doc_number: 1, binder: 1 } }, { path: '/docs', method: 'post', body: { doc_number: 5, binder: 8 } }, { path: '/docs', method: 'post', body: { doc_number: 6, binder: 3 } }, ]
该响应具有状态码207(多状态),如下所示:
[ { path: '/docs', method: 'post', body: { doc_number: 1, binder: 1 } status: 200 }, { path: '/docs', method: 'post', body: { error: { msg: 'A document with doc_number 5 already exists' ... } }, status: 409 }, { path: '/docs', method: 'post', body: { doc_number: 6, binder: 3 }, status: 200 }, ]
你也可以在这个结构中添加对头文件的支持。 我们实现了一些被certificate有用的东西,这些东西是在批处理请求之间使用的variables,这意味着我们可以使用一个请求的响应作为另一个请求的input。
Facebook和Google有类似的实现:
https://developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests
当你想用相同的调用来创build或者更新一个资源的时候,我会根据情况使用POST或者PUT。 如果文档已经存在,是否希望整个文档是:
- 被您发送的文件replace(即请求中缺less的属性将被删除,并且已经被覆盖)?
- 合并您发送的文件(即在请求中缺less的属性将不会被删除,现有的属性将被覆盖)?
如果你想从备选scheme1的行为,你应该使用POST,如果你想从备选scheme2的行为,你应该使用PUT。
http://restcookbook.com/HTTP%20Methods/put-vs-post/
正如人们已经build议你也可以去做PATCH,但我更喜欢保持API的简单,不要使用额外的动词,如果不需要的话。