REST - ETag when POST to collection of resources and If-Match header

REST - ETag when POST to collection of resources and If-Match header

每个用户都有 collection 个 gem。这些 gem 可以通过 REST API:

访问
GET    /user/<user id>/gem              -> get all gems
GET    /user/<user id>/gem/<gem id>     -> get an existing gem
POST   /user/<user id>/gem              -> add a new gem
PUT    /user/<user id>/gem/<gem id>     -> edit an existing gem
DELETE /user/<user id>/gem/<gem id>     -> delete an existing gem

我有几个后端进程,它们 运行 并发并且可以通过 POST HTTP 方法添加 gems。 (他们也可以编辑(PUT)或删除(DELETE)gems,但这对我的问题并不重要。实际上,它很重要。请继续阅读。)

从 high-level 的角度来看,他们执行以下操作:

1.  GET /user/<current user id>/gem
2.  some calculations, based on step 1
3a. if (step 2 decided that a gem should be added)
3b.     POST /user/<current user id>/gem

如前所述,这些过程 运行 并行进行。通常,两个进程不管理同一用户的 gem,但它可能会发生。

所以我需要一种机制来禁止 POST 步骤 3b 如果在此期间发生了某些变化。我考虑过使用 ETags 和乐观锁定:

1.  GET /user/<current user id>/gem and remember the returned ETag
2.  some calculations, based on step 1
3a. if (step 2 decided that a gem should be added)
3b.     POST /user/<current user id>/gem with header 'If-Match=<ETag from step 1>'
3c.     if (server returns 412 - precondition failed)
3d.         start again at step 1

我不确定 ETag 是否适用于此。大多数 ETag 的例子都是关于一个单一的资源(例如 /gem/23),而不是关于 collection 的资源(例如 /gem)。也就是说,在步骤 3b 中,我提供了 gem 的完整 collection 的 ETag,而实际上我提供了一个要添加的 gem。

当然,您的问题听起来非常适合 ETag 的使用。它是一个集合这一事实并不重要;重要的是它是一种资源,您可以从中始终如一地生成 ETag。

要考虑的一件事是,如果集合的表示有任何可以更改的内容,而这对您的 API 消费者来说并不重要。例如,假设您以某种随机或不一致的顺序输出集合,但集合在语义上定义为无序。在这种情况下,您可以根据集合中的项目集生成 weak ETag