30

我们都知道通过 REST 删除单个项目的“标准”方式是向 URI 发送单个 DELETE 请求example.com/Items/666。大,让我们继续一次删除许多。由于我们不需要原子删除(或真正的事务,即全有或全无),我们可以告诉客户“运气不好,提出许多请求”,但这不是很好。所以我们需要一种方法来允许客户端请求一次删除许多“项目”。

据我了解,这个问题的“典型”解决方案是“两步”方法。首先,客户端 POST 一个项目 ID 列表并返回一个 URI,例如example.com/Items/Collection/1. 创建该集合后,他们会对其调用 DELETE。

现在,我发现这很好用,但对我来说,这是一个糟糕的解决方案。首先,您强制客户端发出两个请求以适应服务器。其次,“我认为 DELETE 应该删除一个项目?”,不应该在这个 URI 上调用 DELETE 有效地取消事务(虽然它不是真正的事务),我们怎么会取消它呢?如果有某种形式的“执行”动作真的会更好,但我不能那么摇摆不定。它还迫使服务器必须考虑“发布的 JSON 看起来更像是修改这些项目的请求,但请求是删除......所以我想我会删除它们”。这种方法也开始在客户端/服务器上强加一种状态,我承认不是一个真实的状态,但它是一种。

在我看来,更好的解决方案是简单地调用 DELETE example.com/Items(或者example.com/Items/Collection暗示这是多次删除)并传递包含您希望删除的 ID 列表的 JSON 数据。据我所知,这基本上解决了第一种方法的所有问题。它更容易用作客户端,减少了服务器必须做的工作,真正无状态,更具语义化。

我真的很感激对此的反馈,我是否遗漏了一些关于 REST 的东西,这使得我对这个问题的解决方案变得不切实际?我也很欣赏文章的链接,特别是如果他们比较这两种方法;我知道这通常不被批准用于 SO。我需要能够反驳只有第一种方法是真正的 RESTfull,证明第二种方法是可行的解决方案。当然,如果我叫错了树,请告诉我。

4

3 回答 3

25

I have spent the last week or so reading a fair bit on REST, and to the best of my understanding, it would be wrong to describe either of these solutions as 'RESTfull', rather you should say that 'neither solution goes against what REST means'.

The short answer is simply that REST, as laid out in Roy Fielding's dissertation (See chapter 5), does not cover the topic of how to go about deleting resources, singular or multiple, in a REST manor. That's right, there is no 'correct RESTful way to delete a resource'... well, not quite.

REST itself does not define how delete a resource, but it does define that what ever protocol you are using (remember that REST is not a protocol) will dictate the how perform these actions. The protocol will usually be HTTP; 'usually' being the key word as Fielding will point out, REST is not synonymous with HTTP.

So we look to HTTP to say which method is 'right'. Sadly, as far as HTTP is concerned, both approaches are viable. Yes 'viable'. HTTP will allow a client to send a POST request with a payload (to create a collection resource), and then call a DELETE method on this new collection to delete the resources; it will also allow you to send the data within the payload of a single DELETE method to delete the list of resources. HTTP is simply the medium by which you send requests to the server, it would be up to the server to respond appropriately. To me, the HTTP protocol seems to be rather open to interpretation in places, but it does seem to lay down fairly clear guide lines for what actions mean, how they should be dealt with and what response should be given; it's just it is a 'you should do this' rather than 'you must do this', but perhaps I am being a little pedantic on the wording.

Some people would argue that the 'two stage' approach cannot possibly be 'REST' as the server has to store a 'state' for the client to perform the second action. This is simply a misunderstanding of some part. It must be understood that neither the client nor the server is storing any 'state' information about the other between the list being POSTed and then subsequently being DELETEd. Yes, the list must have been created before it can deleted, but the server does not remember that it was client alpha that made this list (such an approach would allow the client to simply call 'DELETE' as the next request and the server remembers to use that list, this would not be stateless at all) as such, the client must tell the server to DELETE that specific list, the list it was given a specific URI for. If the client attempted to DELETE a collection list that did not already exist it would simply be told 'the resource can not be found' (the classic 404 error most likely). If you wish to claim that this two step approach does maintain a state, you must also claim that to simply GET an URI requires a state, as the URI must first exist. To claim that there is this 'state' persisting is misunderstanding what 'state' means. And as further 'proof' that such a two stage approach is indeed stateless, you could quite happily have client alpha POST the list and later client beta (without having had any communication with the other client) call DELETE on the list resources.

I think it can stand rather self evident that the second option, of just sending the list in the payload of the DELETE request, is stateless. All the information required to complete the request is stored completely within the one request.

It could be argued though that the DELETE action should only be called on a 'tangible' resource, but in doing so you are blatantly ignoring the REpresentational part of REST; It's in the name! It is the representational aspect that 'permits' URIs such as http://example.com/myService/timeNow, a URI that when 'got' will return, dynamically, the current time, with out having to load some file or read from some database. It is a key concept that the URIs are not mapping directly to some 'tangible' piece of data.

There is however one aspect of that stateless nature that must be questioned. As Fielding describes the 'client-stateless-server' in section 5.1.3, he states:

We next add a constraint to the client-server interaction: communication must
  be stateless in nature, as in the client-stateless-server (CSS) style of
  Section 3.4.3 (Figure 5-3), such that each request from client to server must
  contain all of the information necessary to understand the request, and
  cannot take advantage of any stored context on the server. Session state is
  therefore kept entirely on the client.

The key part here in my eyes is "cannot take advantage of any stored context on the server". Now I will grant you that 'context' is somewhat open for interpretation. But I find it hard to see how you could consider storing a list (either in memory or on disk) that will be used to give actual useful meaning would not violate this 'rule'. With out this 'list context' the DELETE operation makes no sense. As such, I can only conclude that making use of a two step approach to perform an action such as deleting multiple resources cannot and should not be considered 'RESTfull'.

I also begrudge somewhat the effort that has had to be put into finding arguments either way for this. The Internet at large seems to have become swept up with this idea the the two step approach is the 'RESTfull' way doing such actions, with the reasoning 'it is the RESTfull way to do it'. If you step back for a moment from what everybody else is doing, you will see that either approach requires sending the same list, so it can be ignored from the argument. Both approaches are 'representational' and 'stateless'. The only real difference is that for some reason one approach has decided to require two requests. These two requests then come with follow up questions, such as how 'long do you keep that data for' and 'how does a client tell a server that it no longer wants that this collection, but wishes to keep the actual resources it refers to'.

So I am, to a point, answering my question with the same question, 'Why would you even consider a two step approach?'

于 2013-06-27T13:49:56.817 回答
3

海事组织:

现有集合上的 HTTP DELETE 删除其所有成员似乎很好。创建集合只是为了删除所有成员听起来很奇怪。正如您自己建议的那样,只需使用 JSON(或任何其他有效负载格式)传递要删除的项目的 ID。我认为服务器应该尝试多次删除内部事务。

于 2013-07-23T13:39:22.053 回答
1

我认为 HTTP已经提供了一种以持久连接和流水线的形式删除多个项目的方法。在 HTTP 协议级别,以流水线方式请求 DELETE 等幂等方法绝对没问题 - 也就是说,在单个连接上一次发送所有 DELETE 请求并等待所有响应。

这对于在浏览器中运行的 AJAX 客户端可能会有问题,因为很少有浏览器默认启用流水线支持。但是,这不是 HTTP 的错,而是那些特定客户端的错。

于 2014-07-07T19:31:06.680 回答