Cách hiệu quả để xóa nhiều mục


97

Trong bài viết wiki cho REST , chỉ ra rằng nếu bạn sử dụng http://example.com/resources DELETE, điều đó có nghĩa là bạn đang xóa toàn bộ bộ sưu tập.

Nếu bạn sử dụng http://example.com/resources/7HOU57Y DELETE, điều đó có nghĩa là bạn đang xóa phần tử đó.

Tôi đang làm một TRANG WEB, lưu ý KHÔNG PHẢI LÀ DỊCH VỤ WEB.

Tôi có một danh sách có 1 hộp kiểm cho mỗi mục trong danh sách. Khi tôi chọn nhiều mục để xóa, tôi sẽ cho phép người dùng nhấn một nút gọi là CHỌN XÓA. Nếu người dùng nhấn nút, một hộp thoại js sẽ bật lên yêu cầu người dùng xác nhận việc xóa. nếu người dùng xác nhận, tất cả các mục sẽ bị xóa.

Vì vậy, làm thế nào tôi nên phục vụ cho việc xóa nhiều mục một cách RESTFUL?

LƯU Ý, hiện tại đối với DELETE trong một trang web, những gì tôi làm là sử dụng thẻ FORM với hành động POST nhưng bao gồm một _method với giá trị DELETE vì đây là những gì đã được chỉ ra bởi những người khác trong SO về cách thực hiện RESTful delete cho trang web .


1
Điều quan trọng là việc xóa này được thực hiện nguyên tử? Bạn có thực sự muốn đảo ngược việc xóa 30 mục đầu tiên nếu mục 31 không thể xóa?
Darrel Miller

@darrelmiller câu hỏi hay. Tôi đã nghĩ nếu quá trình xóa được thực hiện nguyên tử, nó sẽ kém hiệu quả hơn. Do đó, tôi nghiêng về XÓA khỏi tên bảng MÀ ID VÀO ({danh sách id}). Nếu ai đó có thể chỉ ra cho tôi liệu đây có phải là một ý tưởng tốt hay không hoặc sửa chữa cho tôi. điều đó sẽ được đánh giá cao. Ngoài ra, tôi không yêu cầu đảo ngược việc xóa đối với 20 mục đầu tiên nếu mục thứ 21 bị xóa. Một lần nữa, tôi đánh giá cao nếu ai đó có thể chỉ cho tôi sự khác biệt trong cách tiếp cận nơi tôi cần đảo ngược so với nơi tôi KHÔNG cần đảo ngược
Kim Stacks

1
Lưu ý: có thể có giới hạn cho mệnh đề "IN"; ví dụ, trong Oracle, bạn có thể đặt tối đa 1000 id.
cướp

Của Google thiết kế API dẫn Mời một giải pháp để tạo ra tùy chỉnh (batch) hoạt động trong một REST API, xem câu trả lời của tôi ở đây: stackoverflow.com/a/53264372/2477619
B12Toaster

Câu trả lời:


53

Tôi nghĩ câu trả lời của rojoca là tốt nhất cho đến nay. Một biến thể nhỏ có thể là, loại bỏ xác nhận javascript trên cùng một trang và thay vào đó, tạo vùng chọn và chuyển hướng đến nó, hiển thị thông báo xác nhận trên trang đó. Nói cách khác:

Từ:
http://example.com/resources/

làm một

ĐĂNG với lựa chọn ID để:
http://example.com/resources/selices

mà, nếu thành công, nên trả lời bằng:

HTTP / 1.1 201 được tạo và tiêu đề Vị trí để:
http://example.com/resources/selices/DF4XY7

Trên trang này, bạn sẽ thấy hộp xác nhận (javascript), hộp này nếu bạn xác nhận sẽ thực hiện yêu cầu:

XÓA BỎ http://example.com/resources/selutions/DF4XY7

mà, nếu thành công, sẽ phản hồi bằng: HTTP / 1.1 200 Ok (hoặc bất kỳ điều gì thích hợp để xóa thành công)


Tôi thích ý tưởng này vì bạn không cần bất kỳ chuyển hướng nào. Kết hợp AJAX, bạn có thể làm tất cả điều này mà không cần rời khỏi trang.
rojoca

Sau khi DELETE example.com/resources/selutions/DF4XY7 này , tôi có được chuyển hướng trở lại example.com/resources không?
Kim Stacks

7
@fireeyeboy Cách tiếp cận hai bước này dường như là một cách thường được đề xuất để thực hiện nhiều lần xóa, nhưng tại sao? Tại sao bạn không đơn giản gửi một yêu cầu XÓA đến một tiểu yêu cầu http://example.com/resources/selections/và trong phần tải trọng (nội dung) của yêu cầu, bạn gửi dữ liệu cho những mục nào bạn muốn xóa. Theo như tôi có thể nói, không có gì ngăn cản bạn làm điều này, nhưng tôi luôn gặp phải "nhưng nó không phải là RESTfull".
thecoshman

6
DELETE có thể có khả năng đã cơ thể bị bỏ qua bởi HTTP cơ sở hạ tầng: stackoverflow.com/questions/299628/...
Luke Puplett

DELETE có thể có một phần thân, nhưng rất nhiều triển khai của nó đã cấm phần thân của nó theo mặc định
dmitryvim

54

Một tùy chọn là tạo một "giao dịch" xóa. Vì vậy, bạn POSTđến một cái gì đó giống như http://example.com/resources/deletesmột tài nguyên mới bao gồm một danh sách các tài nguyên sẽ bị xóa. Sau đó, trong ứng dụng của bạn, bạn chỉ cần thực hiện xóa. Khi bạn làm bài, bạn nên trả về một vị trí của ví dụ giao dịch tạo của bạn, http://example.com/resources/deletes/DF4XY7. A GETtrên điều này có thể trả về trạng thái của giao dịch (đã hoàn tất hoặc đang diễn ra) và / hoặc danh sách các tài nguyên sẽ bị xóa.


2
Không có gì để làm với cơ sở dữ liệu của bạn. Theo giao dịch, ý tôi chỉ là một danh sách các hoạt động cần thực hiện. Trong trường hợp này, nó là một danh sách xóa. Những gì bạn làm là tạo một danh sách mới (xóa) làm tài nguyên trong ứng dụng của bạn. Ứng dụng web của bạn có thể xử lý danh sách đó theo cách bạn muốn. Tài nguyên đó có URI, ví dụ: example.com/resources/deletes/DF4XY7 . Điều này có nghĩa là bạn có thể kiểm tra trạng thái của việc xóa thông qua GET tới URI đó. Điều này sẽ hữu ích nếu khi thực hiện xóa, bạn phải xóa hình ảnh khỏi Amazon S3 hoặc một số CDN khác và thao tác đó có thể mất nhiều thời gian để hoàn thành.
rojoca

2
+1 đây là một giải pháp tốt. Thay vì gửi một DELETE cho từng tài nguyên, @rojoca đề xuất tạo một phiên bản của loại tài nguyên mới có nhiệm vụ duy nhất là xóa danh sách tài nguyên. Ví dụ: bạn có một bộ sưu tập tài nguyên người dùng và bạn muốn xóa Người dùng Bob, Dave và Amy khỏi bộ sưu tập của mình, vì vậy bạn tạo một tài nguyên Xóa mới ĐĂNG Bob, Dave và Amy làm thông số tạo. Tài nguyên Xóa được tạo và đại diện cho quá trình không đồng bộ để xóa Bob, Dave và Amy khỏi bộ sưu tập Người dùng.
Mike Tunnicliffe,

1
Tôi xin lỗi. Tôi vẫn gặp một chút khó khăn trong việc hiểu một số vấn đề. DF4XY7. Làm thế quái nào bạn tạo ra chuỗi này? Tài nguyên xóa này. Tôi có cần chèn bất kỳ dữ liệu nào vào cơ sở dữ liệu không? Tôi xin lỗi nếu tôi lặp lại một số câu hỏi. Nó chỉ là một chút xa lạ đối với tôi.
Kim Stacks

1
Tôi giả sử DF4XY7 là một id duy nhất được tạo, có lẽ sẽ tự nhiên hơn nếu chỉ sử dụng id được tạo khi lưu vào DB, ví dụ example.com/resources/deletes/7. Việc của tôi là tạo mô hình Xóa và lưu nó trong cơ sở dữ liệu, bạn có thể có quy trình không đồng bộ xóa các bản ghi khác cập nhật mô hình Xóa với trạng thái hoàn thành và bất kỳ lỗi nào có liên quan.
Mike Tunnicliffe,

2
@rojoca vâng, tôi nghĩ vấn đề là HTTP rất nhiều 'DELETE là để xóa một tài nguyên duy nhất'. Bạn đã từng làm gì, bị xóa nhiều lần là một điều hơi khó. Bạn vẫn có thể trả lại 'công việc' cho khách hàng nói rằng tác vụ này đang được thực hiện (và có thể mất một chút thời gian) nhưng hãy sử dụng URI này để kiểm tra tiến độ. Tôi đọc thông số kỹ thuật và hiểu rằng DELETE có thể có một phần thân, giống như các yêu cầu khác.
thecoshman

33

Đây là những gì Amazon đã làm với API REST S3 của họ.

Yêu cầu xóa cá nhân:

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))

Yêu cầu Xóa Nhiều Đối tượng :

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>           

Nhưng API đồ thị Facebook , API REST máy chủ phân tích cú phápAPI REST Google Drive còn đi xa hơn nữa bằng cách cho phép bạn "thực hiện hàng loạt" các hoạt động riêng lẻ trong một yêu cầu.

Đây là một ví dụ từ Máy chủ phân tích cú pháp.

Yêu cầu xóa cá nhân:

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

Yêu cầu hàng loạt:

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

13

Tôi sẽ nói XÓA http://example.com/resources/id1,id2,id3,id4 hoặc XÓA http://example.com/resources/id1+id2+id3+id4 . Vì "REST là một kiến ​​trúc (...) [không phải] giao thức" để trích dẫn bài báo wikipedia này, tôi tin rằng, không có một cách duy nhất nào để làm điều này.

Tôi biết rằng ở trên không thể thực hiện được nếu không có JS với HTML nhưng tôi có cảm giác rằng REST là:

  • Được tạo mà không cần nghĩ đến các chi tiết nhỏ như giao dịch. Ai sẽ cần phải hoạt động trên nhiều mặt hàng hơn? Điều này bằng cách nào đó được chứng minh trong giao thức HTTP vì nó không nhằm phục vụ thông qua nó bất kỳ thứ gì khác sau đó là các trang web tĩnh.
  • Không cần thiết phải điều chỉnh tốt các mô hình hiện tại - ngay cả với HTML thuần túy.

thx - điều gì xảy ra nếu bạn muốn xóa toàn bộ bộ sưu tập - sau đó có nên bỏ qua các ID không?
BKSpurgeon

“Tôi có cảm giác rằng REST đã được… tạo ra mà không cần nghĩ đến những chi tiết nhỏ như giao dịch” - Tôi không nghĩ điều đó hoàn toàn đúng. Nếu tôi hiểu đúng, trong REST, các giao dịch được thể hiện bằng tài nguyên, không phải bằng một phương thức. Có một số cuộc thảo luận tốt mà đỉnh điểm là bình luận này trên bài đăng blog này .
Paul D. Waite

10

Thật thú vị, tôi nghĩ rằng phương pháp tương tự cũng áp dụng cho việc PATCHing nhiều thực thể và yêu cầu suy nghĩ về ý nghĩa của chúng tôi với URL, tham số và phương thức REST.

  1. trả về tất cả các phần tử 'foo':

    [GET] api/foo

  2. trả về các phần tử 'foo' với tính năng lọc cho các id cụ thể:

    [GET] api/foo?ids=3,5,9

Trong đó ý nghĩa là URL và bộ lọc xác định "những phần tử chúng tôi đang xử lý?", Và phương thức REST (trong trường hợp này là "GET") cho biết "phải làm gì với những phần tử đó?"

  1. Do đó PATCH nhiều bản ghi để đánh dấu chúng là đã đọc

    [PATCH] api/foo?ids=3,5,9

..with data foo [read] = 1

  1. Cuối cùng để xóa nhiều bản ghi, điểm cuối này hợp lý nhất:

    [DELETE] api/foo?ids=3,5,9

Xin hãy hiểu rằng tôi không tin rằng có bất kỳ "quy tắc" nào về điều này - đối với tôi nó chỉ "có lý"


Thực ra liên quan đến PATCH: vì nó có nghĩa là cập nhật một phần nếu bạn nghĩ về danh sách các thực thể như một thực thể (ngay cả khi thuộc kiểu mảng), gửi một mảng một phần (chỉ id bạn muốn cập nhật) của các thực thể một phần, thì bạn có thể bỏ qua chuỗi truy vấn, do đó không có URL đại diện cho nhiều hơn một thực thể.
Szabolcs Páll

2

Như câu trả lời của Decent Dabblercâu trả lời rojocas đã nói, quy tắc chuẩn nhất là sử dụng tài nguyên ảo để xóa lựa chọn tài nguyên, nhưng tôi nghĩ rằng điều đó không chính xác từ góc độ REST, bởi vì việc thực thi a DELETE http://example.com/resources/selections/DF4XY7sẽ xóa chính tài nguyên lựa chọn, không phải tài nguyên đã chọn.

Lấy Maciej Piechotka anwser hoặc câu trả lời fezfox , tôi chỉ có một phản đối: Có một cách chuẩn hơn để chuyển một mảng id và sử dụng toán tử mảng:

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

Bằng cách này, bạn đang tấn công vào điểm cuối Xóa Bộ sưu tập nhưng lọc việc xóa bằng chuỗi truy vấn theo đúng cách.


-1

Vì không có cách 'thích hợp' để làm điều này, những gì tôi đã làm trong quá khứ là:

gửi DELETE tới http://example.com/something với dữ liệu được mã hóa xml hoặc json trong nội dung.

Khi bạn nhận được yêu cầu, hãy kiểm tra DELETE, nếu đúng, sau đó đọc phần nội dung để biết những yêu cầu bị xóa.


Đây là cách tiếp cận có ý nghĩa đối với tôi, bạn chỉ cần gửi dữ liệu trong một yêu cầu, nhưng tôi luôn gặp phải "nhưng nó không phải RESTfull". Bạn có bất kỳ nguồn nào cho thấy đây là một phương pháp khả thi và 'RESTfull' để thực hiện việc này không?
thecoshman

10
Vấn đề với cách tiếp cận này là các hoạt động DELETE không mong đợi một phần thân và vì vậy một số bộ định tuyến trung gian trên internet có thể xóa nó cho bạn mà bạn không kiểm soát hoặc không biết. Vì vậy, sử dụng cơ thể để XÓA là không an toàn!
Alex White

Tham khảo nhận xét của Alex: stackoverflow.com/questions/299628/…
Luke Puplett,

1
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.từ tools.ietf.org/html/rfc7231#section-4.3.5
cottton

-1

Tôi đã gặp trường hợp tương tự để xóa nhiều mục. Đây là những gì tôi đã kết thúc. Tôi đã sử dụng thao tác DELETE và id của các mục sẽ bị xóa là một phần của tiêu đề HTTP.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.