ChatGPT解决这个技术问题 Extra ChatGPT

Restful way for deleting a bunch of items

In wiki article for REST it is indicated that if you use http://example.com/resources DELETE, that means you are deleting the entire collection.

If you use http://example.com/resources/7HOU57Y DELETE, that means you are deleting that element.

I am doing a WEBSITE, note NOT WEB SERVICE.

I have a list that has 1 checkbox for each item on the list. Once i select multiple items for deletion, i will allow users to press a button called DELETE SELECTION. If user presses the button, a js dialog box will popup asking user to confirm the deletion. if user confirms, all the items are deleted.

So how should i cater for deleting multiple items in a RESTFUL way?

NOTE, currently for DELETE in a webpage, what i do is i use FORM tag with POST as action but include a _method with the value DELETE since this is what was indicated by others in SO on how to do RESTful delete for webpage.

Is it critical that these deletes be performed atomically? Do you really want to reverse the deletion of the the first 30 items if the the 31st cannot be deleted?
@darrelmiller good question. I thought if the deletes are performed atomically, it will be less efficient. Hence I am leaning towards DELETE FROM tablename WHERE ID IN ({list of ids}). If someone can point out to me whether this is a good idea or correct me. that would be well appreciated. Also i do not require the reverse of the deletion for first 20 items if the 21st is deleted. Again i appreciate it if someone can show me the difference in approach where i need to reverse versus where i do NOT need to reverse
Note: there may be limits for the "IN" clause; for instance, in Oracle you can put a maximum of 1000 ids.
Google's API design guide offers a solution to create custom (batch) operations in a REST API, see my answer here: stackoverflow.com/a/53264372/2477619

r
rojoca

One option is to create a delete "transaction". So you POST to something like http://example.com/resources/deletes a new resource consisting of a list of resources to be deleted. Then in your application you just do the delete. When you do the post you should return a location of your created transaction e.g., http://example.com/resources/deletes/DF4XY7. A GET on this could return the status of the transaction (complete or in progress) and/or a list of resources to be deleted.


Nothing to do with your database. By transaction I just mean a list of operations to perform. In this case it is a list of deletes. What you do is create a new list (of deletes) as a resource in your application. Your web application can process that list however you want. That resource has a URI e.g., example.com/resources/deletes/DF4XY7. This means you could check on the status of the delete via a GET to that URI. This would be handy if when you did a delete you had to delete images from Amazon S3 or some other CDN and that operation might take a long time to complete.
+1 this is a nice solution. Instead of sending a DELETE to each resource, @rojoca proposes creating an instance of a new type of resource whose sole task is deleting a list of resources. For example, you have a collection of user resources and you want to delete Users Bob, Dave and Amy from your collection, so you create a new Deletion resource POSTing Bob, Dave and Amy as the creation parameters. The Deletion resource is created, and represents the asynchronous process of deleting Bob, Dave and Amy from the Users collection.
I am sorry. I still have some slight difficulty in understanding a few issues. the DF4XY7. how on earth do you generate this string? This Deletion resource. Do i need to insert any data into the database? I apologise if i repeat some questions. It is just a little unfamiliar to me.
I assume DF4XY7 is a generated unique id, perhaps it is more natural to just use the id generated when saved to the DB, for example example.com/resources/deletes/7. My take would be to create the Deletion model and save it in the database, you can have the asynchronous process deleting the other records update the Deletion model with the completion status, and any relevant errors.
@rojoca yeah, I think the problem is HTTP is very much 'DELETE is for removing a single resource'. What ever you do, getting multiple delete is a bit of a hack. You can still return a 'job' to the client saying that this task is being worked on (and could take some time) but use this URI to check the progress. I read the spec and took it that DELETE can have a body, just like other requests.
J
Jonnny

I think rojoca's answer is the best so far. A slight variation might be, to do away with the javascript confirm on the same page, and instead, create the selection and redirect to it, showing a confirm message on that page. In other words:

From:
http://example.com/resources/

do a

POST with a selection of the ID's to:
http://example.com/resources/selections

which, if successful, should respond with:

HTTP/1.1 201 created, and a Location header to:
http://example.com/resources/selections/DF4XY7

On this page you will then see a (javascript) confirm box, which if you confirm will do a request of:

DELETE http://example.com/resources/selections/DF4XY7

which, if successful, should respond with: HTTP/1.1 200 Ok (or whatever is appropriate for a successful delete)


I like this idea because you don't need any redirects. Incorporating AJAX you could do this all without leaving the page.
After this DELETE example.com/resources/selections/DF4XY7, would i be redirected back to the example.com/resources?
@fireeyeboy This two step approach seems to be such a commonly suggest way of performing a multi-delete, but why? Why do you not simple send a DELETE request to a uri like http://example.com/resources/selections/ and in the payload (body) of the request you send the data for which items you wish to delete. As far I as I can tell, there is nothing preventing you from doing this, but I always get met with "but it's not RESTfull".
DELETE can potentially have the body ignored by HTTP infrastructure: stackoverflow.com/questions/299628/…
DELETE can have a body, but a lot of its implementations forbidden its body by default
L
Luka Žitnik

Here's what Amazon did with their S3 REST API.

Individual delete request:

DELETE /ObjectName HTTP/1.1
Host: BucketName.s3.amazonaws.com
Date: date
Content-Length: length
Authorization: authorization string (see Authenticating Requests (AWS Signature Version 4))

Multi-Object Delete request:

POST /?delete HTTP/1.1
Host: bucketname.s3.amazonaws.com
Authorization: authorization string
Content-Length: Size
Content-MD5: MD5

<?xml version="1.0" encoding="UTF-8"?>
<Delete>
    <Quiet>true</Quiet>
    <Object>
         <Key>Key</Key>
         <VersionId>VersionId</VersionId>
    </Object>
    <Object>
         <Key>Key</Key>
    </Object>
    ...
</Delete>           

But Facebook Graph API, Parse Server REST API and Google Drive REST API go even further by enabling you to "batch" individual operations in one request.

Here's an example from Parse Server.

Individual delete request:

curl -X DELETE \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

Batch request:

curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "requests": [
          {
            "method": "POST",
            "path": "/1/classes/GameScore",
            "body": {
              "score": 1337,
              "playerName": "Sean Plott"
            }
          },
          {
            "method": "POST",
            "path": "/1/classes/GameScore",
            "body": {
              "score": 1338,
              "playerName": "ZeroCool"
            }
          }
        ]
      }' \
  https://api.parse.com/1/batch

f
fezfox

Interestingly, i think the same method applies as to PATCHing multiple entities, and requires thinking about what we mean with our URL, parameters and REST method.

return all 'foo' elements: [GET] api/foo return 'foo' elements with filtering for specific ids: [GET] api/foo?ids=3,5,9

Wherein the sense is the URL and the filter determine "what elements we are dealing with?", and the REST method (in this case "GET") says "what to do with those elements?"

Hence PATCH multiple records to mark them as read [PATCH] api/foo?ids=3,5,9

..with the data foo[read]=1

Finally to delete multiple records, this endpoint is most logical: [DELETE] api/foo?ids=3,5,9

Please understand I don't believe there are any "rules" on this - to me it just "makes sense"


Actually regarding PATCH: since it's meant to mean partial update if you think about the list of entities as an entity itself (even if of type array), sending a partial array (only the ids you want to update) of partial entities, then you can leave out the query string, thus not having an URL representing more than one entity.
Indeed, just the simple solution. Why people have to insist making things hard on themselves is sometimes beyond me.
s
sashoalm

I would say DELETE http://example.com/resources/id1,id2,id3,id4 or DELETE http://example.com/resources/id1+id2+id3+id4. As "REST is an architecture (...) [not] protocol" to quote this wikipedia article there is, I believe, no single one way of doing this.

I am aware that above is not possible without JS with HTML but I get the feeling that REST was:

Created without thinking of minor details like transactions. Who would need to operate on more then single item? This is somehow justified in HTTP protocol as it was not intended to serve through it anything else other then static webpages.

Not necessary well adjusting into current models - even of pure HTML.


thx - what if you wanted to delete the entire collection - should the IDs then be omitted?
“I get the feeling that REST was... created without thinking of minor details like transactions” — I don’t think that’s quite true. If I understand correctly, in REST, transactions are represented by resources, not by a method. There’s some good discussion that culminates in this comment on this blog post.
m
mangelsnc

As Decent Dabbler answer and rojocas answer says, the most canonical is using virtual resources to delete a selection of resources, but I think that is incorrect from a REST perspective, because executing a DELETE http://example.com/resources/selections/DF4XY7 should remove the selection resource itself, not the selected resources.

Taking the Maciej Piechotka anwser or the fezfox answer, I only have an objection: There's a more canonical way of pass an array of ids, and is using the array operator:

DELETE /api/resources?ids[]=1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d&ids[]=7e8f9a0b-1c2d-3e4f-5a6b-7c8d9e0f1a2b

In this way you are attacking to the Delete Collection endpoint but filtering the deletion with a querystring in the right way.


u
user103219

As there is no 'proper' way to do this, what I have done in the past is:

send DELETE to http://example.com/something with xml or json encoded data in the body.

when you receive the request, check for DELETE, if true, then read the body for the ones to be deleted.


This is the approach that make sense to me, you simply send the data in one request, yet I always get met with "but it's not RESTfull". Have you got any sources suggesting this is a viable and 'RESTfull' method for doing this?
The problem with this approach is that DELETE operations do not expect a body, and so some of the intermediate routers on the internet may remove it for you without your control or knowledge. So using body for DELETE is not safe!
Reference for Alex's comment: stackoverflow.com/questions/299628/…
A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request. from tools.ietf.org/html/rfc7231#section-4.3.5
S
Sherin Syriac

I had the same situation to delete multiple items. This is what I ended up doing. I used DELETE operation and the ids of items which were to be deleted were part of HTTP header.