8

I have a question about HTTP DELETE and REST. I have a resource x. Depending on the state of x, deletion of x does either:

  1. Delete x permanently.
  2. Mark x as deleted. This means that x can reverted later on.

I assume that HTTP DELETE must delete the resource according to the HTTP/REST specifics, instead of marking it as deleted, for example: GET on x must return 404 after the HTTP DELETE has been processed. This means that HTTP DELETE cannot be used for the second situation. How would you model this delete behaviour (both 1 and 2) in a RESTful way?

Then, since some resources can be reverted, this should also be made possible via the REST API. How would you model revert behaviour in a RESTful way?

Let's assume x resides on http://company/api/x/ for simplicity.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Zure Citroen
  • 161
  • 2
  • 6

3 Answers3

5

You could use the trashcan approach.

DELETE http://company/api/x/

causes x to be moved to the trashcan. At which point it could be accessed with,

GET http://company/api/trashcan/x/

and if you wanted to restore it, then take the retrieved representation and do

PUT http://company/api/x/

UPDATE:

Using hypermedia makes it a bit more obvious to the client what they are supposed to do.

GET http://company/api/trashcan/x
=>
200 OK

<x-resource>
  <description>This is the object I deleted</description>
  <link rel="restoreto" href="http://company/api/x/" />
</x-resource>

Having thought about it some more, PUT is really the right method. It is unsafe, idempotent and you know the URI where to restore the file too. If fits the semantics of PUT perfectly. An alternative to rel="restoreto" might be rel="originallocation".

Darrel Miller
  • 139,164
  • 32
  • 194
  • 243
  • 1
    I thought about this before, but I am not really a fan of it. I will try to explain why. The first two steps are OK, the server basically moves the resource to another place. However, I have problems with the third step. There, you are adding the resource again at his initial URI (shouldn't that be a POST instead of PUT by the way? I thought PUT was only meant for updating?). Conceptually, this is not really _reverting_ by its definition. This is simply _adding_. The revert functionality is implemented at client side, instead at server side. I think that is an important difference. – Zure Citroen Jul 20 '11 at 16:18
  • @zune PUT can be used for create as as long as the client knows the URL in advance. I understand your revert vs add issue. Let me think about it, I'll see if I can come up with something better. – Darrel Miller Jul 20 '11 at 18:59
  • I read your update. Thanks. Though, I am still not really convinced. I agree that using hypermedias makes it more clear, but my initial argument still stands: the client is responsible for reverting, he himself has to PUT the resource again. This also means that he could deliver a different version of x in the PUT, which are incorrect by the definition of 'revert'. This means that the server when processing the PUT has to verify that the received contents are exactly equal to the original contents. – Zure Citroen Jul 21 '11 at 14:46
  • 1
    @Zune Ok that makes sense. So then you could fall back to a processing resource. `POST /FileRevertProcessor?url=/company/api/x` – Darrel Miller Jul 21 '11 at 15:51
3

You have a resource that can exist in any of several states (active, marked_for_deletion, deleted) with actions that move the resource from one state to another. This is the classic definition of a state machine. For a given transition, you find yourself asking "what HTTP verb represents that specific transition? do I have to abuse one? is POST a catch-all? can I invent my own?" There is a better way. Expose the state directly, and use GET and PUT to modify it. Let the server figure out how to get from the former state to the new state. For example, you might have a resource:

GET /foo -> {"a": 1, "b": 2, "status": "active"}

You want to change it so that:

GET /foo -> {"a": 1, "b": 2, "status": "marked"}

You might well wonder whether DELETE is appropriate for that, or some custom method, or you can just PUT that new state and be done:

GET /foo -> {"a": 1, "b": 2, "status": "active"}
PUT /foo <- {"a": 1, "b": 2, "status": "marked"}
GET /foo -> {"a": 1, "b": 2, "status": "marked"}
fumanchu
  • 14,419
  • 6
  • 31
  • 36
  • This is a very good and transparant solution. Nice! I guess a 405 error code is returned if the user provides an invalid state transition? The only disadvantage I can find here is that PUT requires that the complete resource is provided, instead of only the delta's (which is why you added fields 'a' and 'b' in the PUT). It's about time HTTP PATCH becomes wide-spread? ;) – Zure Citroen Jul 20 '11 at 16:28
  • If you'd rather not send the complete representation, then PATCH is a fine choice. Another would be to expose a separate resource at /foo/status. – fumanchu Jul 20 '11 at 18:04
  • I would recommend 409 if the user provides a state that has no transition defined from the current state to it. – fumanchu Jul 20 '11 at 18:05
  • I created a new post to extend this model. See http://stackoverflow.com/questions/6776198/rest-model-state-transitions. – Zure Citroen Jul 21 '11 at 12:42
0

RESTful means you can use POST to perform a delete if you wanted to since: Unlike SOAP-based web services, there is no "official" standard for RESTful web services.

In this case, I would change your Mark For Delete to a POST (it's actually an update and not a delete).

Jonathan
  • 1,866
  • 12
  • 10
  • Right, good point, didn't realize that the protocol is open for REST. Two questions: (1) Say the state of the resource denotes that the resource can only be marked as deleted, what would you return when HTTP DELETE is called on that resource (or in the other way around)? A 403 or 405 or something else? (2) What would be the resource to which the POST is sent? Directly to http://company/api/x or something else? – Zure Citroen Jul 20 '11 at 13:42
  • 1: I wouldn't call DELETE, I would call POST and on the DB perform the Update to the record that marks it for deletion (a flag that is read from a trigger or "Garbage collection" job). 2: You can create RESTful endpoints at any endpoint you wish. That's the excellent thing about REST. company/api/person/idgoeshere/markfordelete Then your POST package can contain additional information that you might use to verify it is a legitimate request. – Jonathan Jul 20 '11 at 13:46
  • Also: returning a 403 is a reserved HTTP response (Not enough rights). You can return a standard HTTP response, or create a Response Object yourself that you serialize and return via JSON. RESTful implementations stipulate (generically) that you use verbs and light-weight XML or JSON. That's pretty much REST in a nutshell. – Jonathan Jul 20 '11 at 13:49
  • Thanks a lot for your answer. Your answer to 2 is enlightening. I wasn't very clear about 1. I'll try again (sorry English is not my native language). Say the resource can only be marked as deleted. That is, the user cannot delete it. What would you do when the user calls HTTP DELETE anyway? 405? – Zure Citroen Jul 20 '11 at 14:09
  • Don't offer a HTTP DELETE request at all then. Simply create a POST that marks the record for deletion with a timestamp/audit trail and return a 200 for success, 500 for failure. – Jonathan Jul 20 '11 at 14:17
  • I am not offering it, but I cannot prevent the user from calling it anyway. That's what I meant to say. – Zure Citroen Jul 20 '11 at 14:21
  • Ah I see, then you return a 403 from the DELETE request like you mentioned earlier. (Permission Denied). – Jonathan Jul 20 '11 at 14:56
  • Don't use 403. 405 is the proper response if the DELETE method is not allowed for the resource. See http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-15#section-8.4.4 – fumanchu Jul 20 '11 at 15:03