我们都知道通过REST删除单个项目的“标准”方法是向URI example.com/Items/666
发送一个DELETE请求。盛大,让我们马上删除很多。由于我们不需要原子删除(或真正的交易,即全部或全部),我们可以告诉客户“运气不好,提出许多请求”,但这不是很好。所以我们需要一种方法来允许客户端一次请求删除许多“项目”。
根据我的理解,这个问题的“典型”解决方案是“两步”方法。首先,客户端POST一个项目ID列表,并返回一个URI,如example.com/Items/Collection/1
。一旦创建了该集合,他们就会调用DELETE。
现在,我发现这很好用,除了我,这是一个糟糕的解决方案。首先,您强制客户端发出两个容纳服务器的请求。其次,'我以为DELETE应该删除一个Item?',不应该在这个URI上调用DELETE有效地取消事务(虽然它不是真正的事务),我们怎么会取消它?如果有某种形式的'EXECUTE'动作真的会更好,但我不能摇晃 那条。它还强制服务器必须考虑'POSTed的JSON看起来更像是修改这些Item的请求,但请求是DELETE ...所以我想我会删除它们'。这种方法也开始在客户端/服务器上施加某种状态,而不是我承认的真实状态,但它有点像。
在我看来,更好的解决方案是简单地在example.com/Items
上调用DELETE(或者可能是example.com/Items/Collection
以暗示这是多次删除)并传递包含您希望的ID列表的JSON数据删除。据我所知,这基本上解决了第一种方法的所有问题。它更容易用作客户端,减少服务器必须完成的工作,是真正无状态的,更具语义性。
我真的很感激对此的反馈,我是否遗漏了一些关于REST的问题,这使得我对这个问题的解决方案不切实际?我还希望链接到文章,特别是如果他们比较这两种方法;我知道这通常不被批准用于SO。我需要能够反驳说只有第一种方法才是真正的RESTfull,证明第二种方法是可行的解决方案。当然,如果我正在咆哮错误的树,请告诉我。
答案 0 :(得分:25)
我花了大约一个星期左右的时间阅读REST,并且据我所知,将这些解决方案中的任何一个描述为“RESTfull”是错误的,而你应该说并且这两种解决方案都不符合REST的含义'。
简短的回答是,Roy Fielding's dissertation中所列的REST(参见第5章)并未涉及如何在REST庄园中删除资源(单数或多数)的主题。没错,没有正确的REST方法来删除资源' ......好吧,不完全。
REST本身并未定义如何删除资源,但它确实定义了您正在使用的协议(请记住REST不是协议)将决定执行这些操作的方式。该协议通常是HTTP; '通常'作为Fielding will point out, REST的关键词不是HTTP的同义词。
因此,我们希望通过HTTP来说明哪种方法是正确的'。遗憾的是,就HTTP而言,这两种方法都是可行的。是的'可行的'。 HTTP将允许客户端发送带有效负载的POST请求(以创建集合资源),然后在此新集合上调用DELETE方法以删除资源;它还允许您在单个DELETE方法的有效负载内发送数据以删除资源列表。 HTTP只是您向服务器发送请求的媒介,服务器应该做出适当的响应。对我来说,HTTP协议似乎对某些地方的解释比较开放,但似乎确实为行动的意思,应该如何处理以及应该给出什么样的回应制定了相当明确的指导方针;它只是它应该这样做'而不是“你必须这样做”,但也许我对措辞有点迂腐。
有些人会争辩说“两个阶段”。方法不可能是REST' REST'因为服务器必须存储一个状态'为客户端执行第二个操作。这只是对某些部分的误解。必须理解的是,客户端和服务器都没有存储任何状态'有关正在POST的列表之间的另一个信息,然后是DELETEd。是的,列表必须在删除之前创建,但是服务器不记得是这个列表的客户端alpha(这样的方法将允许客户端简单地调用' DELETE'作为下一个请求和服务器记住使用该列表,这根本不是无状态的)因此,客户端必须告诉服务器删除该特定列表,列表给它一个特定的URI。如果客户端试图删除一个尚不存在的集合列表,则只会被告知“无法找到资源”。 (最有可能是经典的404错误)。如果您希望声称这两步方法确实维护状态,您还必须声明只需要获取URI就需要一个状态,因为URI必须首先存在。声称存在这种状态'坚持不懈是对“状态”的误解。手段。并作为进一步的证明'这样的两阶段方法确实是无状态的,你可以非常高兴地在列表中使用客户端alpha POST并且稍后客户端beta(没有与其他客户端进行任何通信)在列表资源上调用DELETE。
我认为,在DELETE请求的有效负载中发送列表的第二个选项是无状态的,这是非常明显的。完成请求所需的所有信息都完全存储在一个请求中。
虽然可以说,DELETE操作只应在有形的'上进行调用。资源,但在这样做时,你公然无视REST的REpresentational部分;它的名字就在这里!这是代表性的方面,允许'诸如http://example.com/myService/timeNow
之类的URI,一个在获得'之后的URI。将动态返回当前时间,而不必加载某个文件或从某个数据库读取。关键概念是URI不直接映射到某些有形的'一块数据。
然而,必须质疑这种无国籍性质的一个方面。正如菲尔丁描述的那样,客户端无状态服务器'在第5.1.3节中,他说:
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.
我眼中的关键部分是"无法利用服务器上任何存储的上下文"。现在,我将授予您“上下文”#39;有点开放的解释。但我发现很难看出你如何考虑存储一个列表(在内存或磁盘上),这些列表将用于提供实际有用的含义,不会违反这个规则'。没有这个列表上下文' DELETE操作毫无意义。因此,我只能得出结论,使用两步方法来执行诸如删除多个资源之类的操作不能也不应该被认为是RESTfull'。
我也有点吝啬必须付出努力才能找到论据。整个互联网似乎已经被这个想法所笼罩,这两步法就是“RESTfull'做这样的行为的方式,推理'它是RESTfull做的方式'。如果您退出其他人正在做的事情,您会发现任何一种方法都需要发送相同的列表,因此可以从参数中忽略它。这两种方法都是代表性的。和“无国籍的”#。唯一真正的区别是,由于某种原因,一种方法决定需要两个请求。然后,这两个请求会提出后续问题,例如您是否长时间保留这些数据。以及'客户如何告诉服务器它不再需要这个集合,但希望保留它引用的实际资源'。
所以我在某一点上,用同样的问题回答我的问题,'为什么你会考虑采用两步法?'
答案 1 :(得分:3)
IMO:
现有集合上的HTTP DELETE删除其所有成员似乎没问题。创建集合只是为了删除所有成员声音奇怪。正如您自己建议的那样,只需使用JSON(或任何其他有效负载格式)传递要删除的项目的ID。我认为服务器应该尝试多次删除内部事务。
答案 2 :(得分:1)
我认为HTTP 已经提供了一种以persistent connections and pipelining形式删除多个项目的方法。在HTTP协议级别,以流水线方式请求DELETE等幂等方法是绝对正确的 - 也就是说,在单个连接上立即发送所有DELETE请求并等待所有响应。
对于在浏览器中运行的AJAX客户端,这可能会有问题,因为很少有浏览器默认启用流水线支持。这不是HTTP的错,但是,这是那些特定客户的错误。