RESTful undelete

后端 未结 6 531
失恋的感觉
失恋的感觉 2021-01-30 12:54

It is a fairly common requirement to support undeletes or delayed/batched deletions for data services. What I\'m wondering is how to implement this in a RESTful way. I\'m torn b

6条回答
  •  [愿得一人]
    2021-01-30 13:16

    I'm also running in this problem and I've been looking on the Internet for what feels like the best solution. Since none of the main answers I can find seem correct to me, here is my own research results.

    Others are right that the DELETE is the way to go. You could include a flag to determine whether it's immediately a permanent DELETE or a move to the trashcan (and probably only administrators can do an immediate permanent DELETE.)

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

    The backend can then mark the book as deleted. Assuming you have an SQL database, it could be something such as:

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

    As mentioned by others, once the DELETE is done, a GET of the collection does not return that item. In terms of SQL, this means you must make sure not to return an item with a status of deleted.

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

    Also, when you do a GET /api/1/book/33, you must return a 404 or 410. One problem with 410 is that it means Gone Forever (at least that's my understanding of that error code,) so I would return 404 as long as the item exists but is marked as 'deleted' and 410 once it was permanently removed.

    Now to undelete, the correct way is to PATCH. Contrary to a PUT which is used to update an item, the PATCH is expected to be an operation on an item. From what I can see, the operation is expected to be in the payload. For that to work, the resource needs to be accessible in some way. As someone else suggested, you can provide a trashcan area where the book would appear once deleted. Something like this would work to list books that were put in the trashcan:

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

    So, the resulting list would now include book number 33, which you can then PATCH with an operation such as:

    PATCH /api/1/trashcan/books/33
    
    {
        "operation": "undelete"
    }
    

    If you'd like to make the operation more versatile, you could use something such as:

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

    Then the "move" could be used for other changes of URL wherever possible in your interface. (I am working on a CMS where the path to a page is in one table called tree, and each page is in another table called page and has an identifier. I can change the path of a page by moving it between paths in my tree table! This is where a PATCH is very useful.)

    Unfortunately, the RFCs do not clearly define the PATCH, only that it is to be used with an operation as shown above, opposed to a PUT which accepts a payload representing a new version, possibly partial, of the targeted item:

    PUT /api/1/books/33
    
    {
        "title": "New Title Here"
    }
    

    Whereas the corresponding PATCH (if you were to support both) would be:

    PATCH /api/1/books/33
    
    {
        "operation": "replace",
        "field": "title",
        "value": "New Title Here"
    }
    

    I think that supporting that many PATCH operations would be crazy. But I think that a few good examples give a better idea of why PATCH is the correct solution.

    You can think of it as: using patch is to change a virtual field or run a complex operation such as a move which would otherwise require a GET, POST, DELETE (and that's assuming the DELETE is immediate and you could get errors and end up with a partial move...) In a way, the PATCH is similar to having any number of methods. An UNDELETE or MOVE method would work in a similar way, but the RFC clearly says there is a set of standardized methods and you should certainly stick to them and the PATCH gives you plenty of room to not have to add your own methods. Although I did not see anything in the specs saying you should not add your own methods. If you do, though, make sure to clearly document them.

提交回复
热议问题