REST API – 在单个请求中批量创build或更新

假设有两个资源“ BinderDoc关联关系”,这意味着“ Doc和“ Binder独立的。 Doc可能或可能不属于BinderBinder可能是空的。

如果我想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资源。

    在这种情况下,您必须定义您的格式来描述部分更新。

我认为在这种情况下, POSTPATCH非常相似,因为您并不需要描述每个元素的操作。 我想说这取决于发送的表示的格式。

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的示例路由。 在这种情况下,使用POSTPATCH方法发送文档列表将在创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。 如果文档已经存在,是否希望整个文档是:

  1. 被您发送的文件replace(即请求中缺less的属性将被删除,并且已经被覆盖)?
  2. 合并您发送的文件(即在请求中缺less的属性将不会被删除,现有的属性将被覆盖)?

如果你想从备选scheme1的行为,你应该使用POST,如果你想从备选scheme2的行为,你应该使用PUT。

http://restcookbook.com/HTTP%20Methods/put-vs-post/

正如人们已经build议你也可以去做PATCH,但我更喜欢保持API的简单,不要使用额外的动词,如果不需要的话。