RESTful取消删除

时间:2011-12-23 23:56:33

标签: rest http http-delete undelete

支持数据服务的取消删除或延迟/批量删除是一个相当普遍的要求。我想知道的是如何以RESTful方式实现它。我在几个不同的选择之间徘徊(其中没有一个对我来说似乎非常有吸引力)。我认为,这些不同选项的共同点是需要一个API,它返回标记为特定资源类型的已删除的所有资源。

以下是我考虑的一些选项以及他们的一些优点/缺点:

将资源标记为已删除的选项:

  • 使用HTTP DELETE将资源标记为已删除。
  • 使用HTTP PUT / POST更新已删除的标志。这感觉不对,因为它将基本上是删除的内容从HTTP DELETE方法映射到其他HTTP方法。

GET-ing资源标记为删除时的选项:

  • 返回标记为已删除的资源的HTTP状态404。清洁&透明,但我们如何区分真正删除的资源与刚刚标记为已删除的资源之间的区别。
  • 返回HTTP状态410.提供告知差异的方法,但410从技术上说它“应该被认为是永久性的。具有链接编辑功能的客户端应该在用户批准后删除对Request-URI的引用。”这里的“预期”和“应该”这两个词可能有足够的摆动空间。不确定在客户端中支持/理解410的情况。
  • 返回HTTP状态200并包括指示资源被删除的标志字段。这似乎很奇怪,因为首先删除它的想法是因为你实际上想要它不出现。这推动了将已删除的资源过滤到客户端的责任。

包含此已删除资源的回复选项:

  • 忽略已删除的资源。清洁&简单。但是如果你真的想知道已删除的资源怎么办呢。
  • 将它们与表示已删除的字段一起包括在内。这推动了将已删除资源过滤到客户端的责任。如果您只想浏览活动资源或已删除的资源,则会使分页变得棘手。

更新标记为删除的资源时的选项:

  • 使用HTTP状态404.资源是否正确?但是,如何区分标记为已删除的资源和实际删除的资源之间的区别。 404响应中的HTTP正文可以在此消除歧义,但随后客户端将解析/解释您的正文以消除歧义。也许响应标题可能有帮助吗?哪一个?自定义标题?
  • 使用HTTP状态409以及有关必须首先取消删除资源的消息。

取消删除标记为删除的资源的选项:

  • 使用HTTP PUT / POST进行资源的更新操作,并再次将其标记为活动状态。这只有在您没有为资源的GET操作返回HTTP 404时才有效,因为它自从PUT / POST到“未找到”资源(404)后才会生成。(/ li>
  • 使用HTTP PUT / POST进行资源的创建操作。这里的问题是哪些数据优先?在创建操作中发送的数据?或者正在取消删除的数据?将其从任何其他可能返回的查询中过滤掉。然后,如果资源标识符指向标记为已删除的资源,则将创建资源的HTTP PUT / POST视为取消删除。
  • 专用于取消删除标记为删除的资源的独立REST路径。

这绝不是一份详尽的清单。我只是想列举一些在我脑子里蹦蹦跳跳的选项。

我知道如何做到这一点的答案就像往常一样,“它取决于”。我很好奇的是你会用什么资格/要求做出决定?您是如何看待自己实施或实施的?

6 个答案:

答案 0 :(得分:8)

继续阅读本书:RFC 2616-9.7

  The DELETE method requests that the origin server delete the resource 
  identified by the Request-URI. This method MAY be overridden by human 
  intervention (or other means) on the origin server. The client cannot
  be guaranteed that the operation has been carried out, even if the 
  status code returned from the origin server indicates that the action
  has  been completed successfully. However, the server SHOULD NOT 
  indicate success unless, at the time the response is given, if it intends
  to delete the resource or move it to an inaccessible location.

当您删除资源时,服务器应在其旁边标记要删除的资源。它实际上不必删除资源,它只是不能保证已经执行了操作。即便如此,服务器也不应该说它已经被删除了。

  A successful response SHOULD be 200 (OK) if the response includes an entity
  describing the status, 202 (Accepted) if the action has not yet been enacted,
  or 204 (No Content) if the action has been enacted but the response does not
  include an entity.

如果操作延迟,请发送202和描述操作结果的实体。 (想想一个表示服务器延迟删除资源的可轮询的“任务”;理论上它可以永远保留在该状态。)它所要做的就是阻止客户端以原始形式再次检索它。使用410作为响应代码,当“任务”完成或服务器以其他方式删除资源时,返回404。

但是,如果DELETE的语义对于有问题的资源没有意义,可能它不是您正在寻找的删除,而是一个添加状态转换,它会改变资源状态但保持可访问性?在这种情况下,使用PUT / PATCH更新资源并完成。

答案 1 :(得分:4)

我认为解决此问题的最RESTful方法是使用HTTP PUT标记资源以进行删除(并取消删除),然后使用HTTP DELETE永久删除资源。要获取标记为删除的资源列表,我将使用HTTP GET请求中的参数,例如。 ?state=markedForDeletion。 如果您在没有参数的情况下请求标记为删除的资源,我认为您应该返回“404 Not Found”状态。

答案 2 :(得分:3)

短版

您无法使用原始URI上的任何方法RESTful取消删除资源 - 这是不合逻辑的,因为在已删除的资源上尝试的任何操作都应返回404或410.虽然规范中未明确说明,它强烈暗示DELETE方法1的定义(强调增加):

  

实际上,此方法类似于UNIX中的rm命令:it   表示对源的URI映射的删除操作   服务器而不是以前关联的期望   信息将被删除。

换句话说,当您删除资源时,服务器不再将该URI映射到该数据。因此,您无法对其进行PUT或POST以进行更新,例如“将此标记为未删除”等。(请记住,资源定义为URI与某些基础数据之间的映射)。

部分解决方案

由于明确声明基础数据不一定被删除,因此不排除服务器将 URI映射作为DELETE实现的一部分,从而有效地制作可以的备份副本稍后恢复。

您可以拥有一个包含所有已删除项目的“/ deleted /”集合 - 但您将如何实际执行取消删除?也许最简单的RESTful方法是让客户端使用GET检索项目,然后将其POST到所需的URL。

如果您需要将已删除的项目还原到原始位置,该怎么办?如果您使用的是支持它的媒体类型,则可以在/ deleted / collection中对GET的响应中包含原始URI。然后客户端可以使用它进行POST。这样的响应可能在JSON中看起来像这样:

{
    "original-url":"/some/place/this/was/deleted/from",
    "body":<base64 encoded body>
}

然后,客户端可以将该主体POST到该URI以执行取消删除。

或者,如果您的资源定义允许移动的概念(通过更新“location”属性或类似的东西),那么您可以进行部分更新并避免整个对象的往返。或者,做大多数人做的事情并实现类似RPC的操作来告诉服务器移动资源! UnRESTful,是的,但在大多数情况下它可能会正常工作。

您如何决定这些事情

关于如何决定这些事情的问题:您必须考虑删除在您的应用程序上下文中的含义,以及您为什么需要它。在很多应用程序中,没有任何内容被删除,“删除”实际上只是意味着“从所有进一步的查询/列表等中排除此项目,除非我明确取消删除它”。所以,它实际上只是一个元数据或移动操作。在这种情况下,为什么要烦扰HTTP DELETE?一个原因可能是如果你想要一个2层删除 - 一个可撤销的软版本或临时版本,以及一个硬件/永久版本,那么......不是。

如果没有任何特定的应用程序上下文,我倾向于像这样实现它们:

我不想再看到此资源了,为方便起见发布部分更新以将资源标记为“暂时删除”

我不希望任何人能够再次访问此资源,因为它令人尴尬/有罪/花了我钱/等等 HTTP DELETE

要考虑的下一个问题是:永久删除是否应该永久取消映射URI,以便任何人都不能再链接到它,或者是否有必要清除基础数据?显然,如果您保留数据,那么管理员甚至可以恢复“永久”删除的资源(但不是通过任何RESTful接口)。这样做的缺点是,如果数据的所有者确实希望它被清除,那么管理员必须在REST接口之外执行此操作。

答案 3 :(得分:2)

&#34;删除&#34; (破坏)项目也可以被视为资源,对吧?然后我们可以通过以下方式之一访问此资源(例如,对于已删除的用户):

PATCH deleted_users/{id}
PATCH trash/users/{id}
PATCH deleted/users/{id}

或者有些人可能认为这是更安静的方式:

PATCH deleted/{id}?type=users

并且在有效载荷中是这样的:

{ deleted_at: null }

答案 4 :(得分:0)

我也遇到了这个问题,并且一直在互联网上寻找最佳解决方案。由于我找不到最主要的答案对我来说都是正确的,因此这是我自己的研究结果。

其他人认为DELETE是正确的选择。您可以添加一个标志来确定它是立即变为永久性DELETE还是移至垃圾桶(可能只有管理员可以立即变为永久性DELETE。)

DELETE /api/1/book/33
DELETE /api/1/book/33?permanent

然后,后端可以将该书标记为已删除。假设您有一个SQL数据库,则可能是这样的:

UPDATE books SET status = 'deleted' WHERE book_id = 33;

正如其他人所提到的,DELETE完成后,集合中的GET不会返回该项目。就SQL而言,这意味着您必须确保不要返回状态为deleted的项目。

SELECT * FROM books WHERE status <> 'deleted';

另外,当您执行GET /api/1/book/33时,必须返回404或410。410的一个问题是,它意味着 Forever (永远消失了(至少这是我对错误代码的理解) ,),因此只要该项目存在但被标记为'deleted',则我将返回404,一旦该项目被永久删除,则返回410。

现在要取消删除,正确的方法是PATCH。与用于更新项目的PUT相反,PATCH应该是对项目的操作。从我可以看到,该操作预计将在有效负载中进行。为此,必须以某种方式访问​​资源。就像其他人建议的那样,您可以提供一个trashcan区域,该区域一旦删除就将在其中显示。像这样的事情会列出已放入垃圾桶的书:

GET /api/1/trashcan/books

[{"path":"/api/1/trashcan/books/33"}]

因此,结果列表现在将包括书号33,然后您可以使用诸如以下的操作PATCH

PATCH /api/1/trashcan/books/33

{
    "operation": "undelete"
}

如果您想使操作更加通用,可以使用诸如:

PATCH /api/1/trashcan/books/33

{
    "operation": "move",
    "new-path": "/api/1/books/33"
}

然后,在您的界面中,“移动”可用于URL的其他更改。 (我正在CMS上工作,其中页面的路径位于一个名为tree的表中,而每个页面位于另一个名为page的表中并且具有标识符。我可以更改页面的路径通过在我的tree表中的路径之间移动它!PATCH在这里非常有用。)

不幸的是,RFC并未明确定义PATCH,只是将其与上述操作一起使用,而不是PUT,它接受​​表示新版本的有效载荷,可能部分,部分:

PUT /api/1/books/33

{
    "title": "New Title Here"
}

相应的PATCH(如果您要同时支持两者)将是:

PATCH /api/1/books/33

{
    "operation": "replace",
    "field": "title",
    "value": "New Title Here"
}

我认为支持许多PATCH操作会很疯狂。但是我认为,有几个很好的例子可以更好地说明为什么PATCH是正确的解决方案。

您可以将其视为:使用补丁程序可以更改 virtual 字段或运行复杂的操作(例如移动),否则将需要GETPOSTDELETE(并且假设DELETE是立即的,并且您可能会出错并最终会进行部分移动...)在某种程度上,PATCH类似于具有任何数字方法。 UNDELETEMOVE方法将以类似的方式工作,但是RFC明确指出存在一组standardized methods,您应该坚持使用它们,而PATCH给出您有足够的空间不必添加自己的方法。尽管我在规范中没有看到任何内容表明您不应添加自己的方法。但是,如果要这样做,请确保清楚地记录它们。

答案 5 :(得分:0)

我们已强制模型创建一个

POST / modelname /:id /取消删除