REST lựa chọn thay thế hiệu quả cho nội dung yêu cầu DELETE


93

Mặc dù thông số kỹ thuật HTTP 1.1 dường như cho phép các nội dung thông báo trên các yêu cầu DELETE , nó dường như chỉ ra rằng các máy chủ nên bỏ qua nó vì không có ngữ nghĩa xác định cho nó.

4.3 Nội dung thư

Máy chủ NÊN đọc và chuyển tiếp nội dung thư theo bất kỳ yêu cầu nào; nếu phương thức yêu cầu không bao gồm ngữ nghĩa đã xác định cho phần thân thực thể, thì phần thân thông báo NÊN bị bỏ qua khi xử lý yêu cầu.

Tôi đã xem xét một số cuộc thảo luận liên quan về chủ đề này trên SO và hơn thế nữa, chẳng hạn như:

Hầu hết các cuộc thảo luận dường như đồng tình rằng việc cung cấp nội dung thông báo trên một DELETE có thể được phép , nhưng nói chung là không nên.

Hơn nữa, tôi đã nhận thấy một xu hướng trong các thư viện máy khách HTTP khác nhau nơi ngày càng có nhiều cải tiến dường như được ghi lại cho các thư viện này để hỗ trợ các cơ quan yêu cầu trên DELETE. Hầu hết các thư viện dường như bắt buộc, mặc dù đôi khi có một chút kháng cự ban đầu.

Trường hợp sử dụng của tôi yêu cầu việc bổ sung một số siêu dữ liệu bắt buộc trên một DELETE (ví dụ: "lý do" xóa, cùng với một số siêu dữ liệu khác được yêu cầu để xóa). Tôi đã xem xét các tùy chọn sau, không có tùy chọn nào trong số đó có vẻ hoàn toàn phù hợp và phù hợp với thông số kỹ thuật HTTP và / hoặc các phương pháp hay nhất của REST:

  • Nội dung thông báo - Thông số chỉ ra rằng nội dung thông báo trên DELETE không có giá trị ngữ nghĩa; không được hỗ trợ đầy đủ bởi các máy khách HTTP; thực hành không tiêu chuẩn
  • Tiêu đề HTTP tùy chỉnh - Yêu cầu tiêu đề tùy chỉnh thường chống lại các thông lệ tiêu chuẩn ; sử dụng chúng không phù hợp với phần còn lại của API của tôi, không có API nào yêu cầu tiêu đề tùy chỉnh; hơn nữa, không có phản hồi HTTP tốt nào có sẵn để chỉ ra các giá trị tiêu đề tùy chỉnh xấu (có thể là một câu hỏi riêng biệt hoàn toàn)
  • Tiêu đề HTTP tiêu chuẩn - Không có tiêu đề tiêu chuẩn nào phù hợp
  • Tham số truy vấn - Thêm tham số truy vấn thực sự thay đổi URI yêu cầu đang bị xóa; chống lại các thông lệ tiêu chuẩn
  • Phương thức POST - (ví dụ POST /resourceToDelete { deletemetadata }) POST không phải là một tùy chọn ngữ nghĩa để xóa; POST thực sự đại diện cho hành động ngược lại mong muốn (tức là POST tạo tài nguyên cấp dưới; nhưng tôi cần xóa tài nguyên)
  • Nhiều phương pháp - Chia yêu cầu DELETE thành hai hoạt động (ví dụ: PUT xóa siêu dữ liệu, sau đó DELETE) chia một hoạt động nguyên tử thành hai, có khả năng để lại trạng thái không nhất quán. Lý do xóa (và các siêu dữ liệu có liên quan khác) không phải là một phần của bản trình bày tài nguyên.

Sở thích đầu tiên của tôi có lẽ sẽ là sử dụng nội dung thư, thứ hai là tiêu đề HTTP tùy chỉnh; tuy nhiên, như đã chỉ ra, có một số nhược điểm đối với những cách tiếp cận này.

Có bất kỳ khuyến nghị hoặc phương pháp hay nhất nào phù hợp với các tiêu chuẩn REST / HTTP để đưa siêu dữ liệu bắt buộc như vậy vào các yêu cầu DELETE không? Có bất kỳ lựa chọn thay thế nào khác mà tôi chưa xem xét không?


2
Các triển khai nhất định như Jerseykhông cho phép nội dung deleteyêu cầu.
basiljames

Câu trả lời:


44

Mặc dù có một số khuyến nghị không sử dụng nội dung thư cho các yêu cầu XÓA, nhưng cách tiếp cận này có thể phù hợp trong một số trường hợp sử dụng nhất định. Đây là cách tiếp cận mà chúng tôi đã sử dụng sau khi đánh giá các tùy chọn khác được đề cập trong câu hỏi / câu trả lời và sau khi cộng tác với người tiêu dùng dịch vụ.

Mặc dù việc sử dụng nội dung thư không phải là lý tưởng, nhưng không có tùy chọn nào khác hoàn toàn phù hợp. Nội dung yêu cầu DELETE cho phép chúng tôi thêm dễ dàng và rõ ràng ngữ nghĩa xung quanh dữ liệu / siêu dữ liệu bổ sung cần thiết để đi kèm với hoạt động DELETE.

Tôi vẫn sẵn sàng đón nhận những suy nghĩ và thảo luận khác, nhưng tôi muốn kết thúc câu hỏi này. Tôi đánh giá cao những suy nghĩ và thảo luận của mọi người về chủ đề này!


12
Đây là một ý tưởng tồi. Một nơi mà điều này sẽ khiến bạn gặp rắc rối là nếu sau này bạn quyết định sử dụng dịch vụ tăng tốc HTTP như Akamai EdgeConnect. Tôi biết thực tế là EdgeConnect loại bỏ các phần thân khỏi các yêu cầu HTTP DELETE (vì chúng tiêu tốn băng thông có thể không hợp lệ). Nhiều khả năng các dịch vụ tương tự cũng làm như vậy (xem tính năng tăng tốc của Kindle và các dịch vụ tương tự CDN khác). Bạn có thể nên thiết kế lại để không sử dụng động từ HTTP cho dịch vụ của mình. Hầu hết các API không có ý nghĩa gì khi sử dụng HTTP-verbs / classic-REST và các vấn đề vận chuyển động từ HTTP rất khó khắc phục.
Gabe

3
Tôi đồng tình với @Gabe, việc gửi một phần thân với các phương thức không có phần thân theo định nghĩa là một cách chắc chắn để làm mất dữ liệu ngẫu nhiên khi các bit của bạn đi ngang qua các đường ống internet và bạn sẽ gặp rất nhiều khó khăn khi gỡ lỗi nó.
Nicholas Shanks

3
Những nhận xét chống lại việc sử dụng DELETE là không liên quan cho đến khi chúng giải quyết các vấn đề rất hợp lệ mà OP mắc phải. Tôi đang cố gắng hết sức để tuân thủ tinh thần của REST, nhưng việc xóa không có đồng thời lạc quan và xóa không có hoạt động hàng loạt nguyên tử là không thực tế trong các tình huống thực tế. Đây là một sự thiếu hụt nghiêm trọng đối với mô hình REST.
Quarkly

Tôi với @Quarkly về điều này. Tôi không hiểu ý tưởng RESTFUL là gì về cách chúng ta nên kiểm tra đồng thời, v.v. Kiểm tra đồng thời không thuộc về ứng dụng khách.
Dirk Wessels

13

Những gì bạn có vẻ muốn là một trong hai thứ, cả hai đều không phải là một thứ thuần túy DELETE:

  1. Bạn có hai thao tác, một PUTtrong số lý do xóa, theo sau là một phần DELETEcủa tài nguyên. Sau khi bị xóa, bất kỳ ai cũng không thể truy cập nội dung của tài nguyên. 'Lý do' không thể chứa siêu liên kết đến tài nguyên đã xóa. Hoặc là,
  2. Bạn đang cố gắng thay đổi tài nguyên từ state=activesang state=deletedbằng cách sử dụng DELETEphương pháp này. Các tài nguyên có trạng thái = đã xóa bị API chính của bạn bỏ qua nhưng quản trị viên hoặc ai đó có quyền truy cập cơ sở dữ liệu vẫn có thể đọc được. Điều này được cho phép - DELETEkhông phải xóa dữ liệu hỗ trợ cho một tài nguyên, chỉ xóa tài nguyên được hiển thị tại URI đó.

Bất kỳ hoạt động nào yêu cầu nội dung thông báo trên một DELETEyêu cầu có thể được chia nhỏ thành nó chung chung nhất, a POSTđể thực hiện tất cả các tác vụ cần thiết với nội dung thông báo và a DELETE. Tôi thấy không có lý do gì để phá vỡ ngữ nghĩa của HTTP.


2
Điều gì xảy ra nếu PUTlý trí thành công và DELETEtài nguyên không thành công? Làm thế nào để ngăn chặn trạng thái không nhất quán?
Lightman

1
@Lightman PUT chỉ xác định ý định. Nó có thể tồn tại mà không có DELETE tương ứng, điều này có nghĩa là ai đó muốn xóa nhưng không thành công hoặc họ đã đổi ý. Việc đảo ngược thứ tự của các lệnh gọi cũng sẽ cho phép XÓA xảy ra mà không cần lý do - việc cung cấp lý do sau đó sẽ chỉ được coi là chú thích. Vì cả hai lý do này, tôi khuyên bạn nên sử dụng tùy chọn 2 từ các tùy chọn trên, tức là thay đổi trạng thái của bản ghi bên dưới chẳng hạn như khiến tài nguyên HTTP biến mất khỏi URL hiện tại của nó. Thì A garbage collector / admin có thể tẩy hồ sơ
Nicholas Shanks

7

Với tình huống của bạn, tôi sẽ thực hiện một trong các cách tiếp cận sau:

  • Gửi PUT hoặc PATCH : Tôi đang suy luận rằng thao tác xóa là ảo, về bản chất là cần lý do xóa. Do đó, tôi tin rằng cập nhật bản ghi thông qua thao tác PUT / PATCH là một cách tiếp cận hợp lệ, mặc dù nó không phải là thao tác DELETE.
  • Sử dụng các tham số truy vấn : Tiểu tài nguyên không bị thay đổi. Tôi thực sự nghĩ rằng đây cũng là một cách tiếp cận hợp lệ. Câu hỏi bạn đã liên kết nói về việc không cho phép xóa nếu tham số truy vấn bị thiếu. Trong trường hợp của bạn, tôi sẽ chỉ có một lý do mặc định nếu lý do đó không được chỉ định trong chuỗi truy vấn. Tài nguyên sẽ vẫn còn resource/:id. Bạn có thể làm cho nó có thể phát hiện được bằng các tiêu đề Liên kết trên tài nguyên cho từng lý do (với một relthẻ trên mỗi lý do để xác định lý do).
  • Sử dụng điểm cuối riêng biệt theo lý do : Sử dụng url như resource/:id/canceled. Điều này thực sự thay đổi URI Yêu cầu và chắc chắn không phải là RESTful. Một lần nữa, tiêu đề liên kết có thể làm cho điều này có thể khám phá được.

Hãy nhớ rằng REST không phải là luật hay giáo điều. Hãy coi đó là sự hướng dẫn. Vì vậy, nếu không tuân theo hướng dẫn cho miền sự cố của bạn là hợp lý, thì đừng. Chỉ cần đảm bảo rằng người tiêu dùng API của bạn được thông báo về sự khác biệt.


Về việc sử dụng các tham số truy vấn, tôi hiểu rằng truy vấn là một phần của URI Yêu cầu cho mỗi phần 3.2 và do đó việc sử dụng phương pháp này (hoặc tương tự, các điểm cuối riêng biệt) đi ngược lại định nghĩa của phương thức DELETE , chẳng hạn như "tài nguyên được xác định bởi URI Yêu cầu "bị xóa.
shelley

Tài nguyên được xác định bằng đường dẫn uri. Vì vậy, một GET to /orders/:idsẽ trả về cùng một tài nguyên như /orders/:id?exclude=orderdetails. Chuỗi truy vấn chỉ đưa ra gợi ý cho máy chủ - trong trường hợp này là loại trừ chi tiết đơn hàng trong phản hồi (nếu được hỗ trợ). Tương tự, nếu bạn đang gửi DELETE đến /orders/:idhoặc /orders/:id?reason=canceledhoặc /orders/:id?reason=bad_credit, bạn vẫn đang hoạt động trên cùng một tài nguyên cơ bản. Để giữ một 'giao diện thống nhất', tôi sẽ có một lý do mặc định để gửi tham số truy vấn là không cần thiết.
codeprogression

@shelley Bạn đang lo lắng đúng về các chuỗi truy vấn. Chuỗi truy vấn là một phần của URI. Gửi yêu cầu DELETE /foo?123có nghĩa là bạn đang xóa một tài nguyên khác với khi bạn gửi DELETE tới /foo?456.
Nicholas Shanks,

@codeprogression Xin lỗi, nhưng phần lớn những gì bạn nói là sai. Tài nguyên được xác định bởi toàn bộ URI, không chỉ đường dẫn. Các chuỗi truy vấn khác nhau là các tài nguyên khác nhau (theo nghĩa HTTP của từ 'tài nguyên'). Ngoài ra, một lý do mặc định không bắt buộc đối với giao diện đồng nhất. Thuật ngữ đó đề cập đến việc sử dụng GET, PUT, POST, PATCH và DELETE theo cách mà HTTP đã định nghĩa chúng. Điểm chung là giữa các nhà cung cấp (nhà cung cấp tác nhân người dùng, nhà cung cấp API, nhà cung cấp proxy bộ nhớ đệm, ISP, v.v.) chứ không phải trong API của riêng ai (mặc dù điều đó cũng phải thống nhất về thiết kế để người dùng hiểu rõ!).
Nicholas Shanks

@Nicholas Tôi không hiểu bạn cần phải tranh luận về một cuộc thảo luận đã kết thúc ba năm trước. Câu trả lời và nhận xét tôi đưa ra là hợp lệ và chính xác theo quan điểm tập trung vào REST. REST không phải là HTTP (cũng như bất kỳ nhà cung cấp nào triển khai HTTP). Trong bối cảnh của REST, các tài nguyên đều giống nhau. Và như tôi đã nói trong câu trả lời của mình, REST không phải là luật hay giáo điều, mà là hướng dẫn.
codeprogression

0

Tôi khuyên bạn nên bao gồm siêu dữ liệu bắt buộc như một phần của chính cấu trúc phân cấp URI. Một ví dụ (Naive):

Nếu bạn cần xóa các mục nhập dựa trên phạm vi ngày, thay vì chuyển ngày bắt đầu và ngày kết thúc trong nội dung hoặc dưới dạng tham số truy vấn, hãy cấu trúc URI theo cách bạn chuyển thông tin bắt buộc như một phần của URI.

ví dụ

DELETE /entries/range/01012012/31122012 - Xóa tất cả các mục từ ngày 1 tháng 1 năm 2012 đến ngày 31 tháng 12 năm 2012

Hi vọng điêu nay co ich.


5
Không bao gồm các trường hợp như gửi lý do xóa, tức là trường commment.
Kugel

3
Chà. đó là một ý tưởng khủng khiếp. Nếu bạn có quá nhiều dữ liệu meta, nó sẽ làm tăng giới hạn kích thước trên URI.
Balaji Boggaram Ramanarayan

1
Cách tiếp cận này không tuân theo các phương pháp RESTful và không được khuyến nghị vì bạn sẽ có cấu trúc URI phức tạp. Các điểm cuối bị xáo trộn với việc xác định tài nguyên đan xen với dữ liệu meta và theo thời gian sẽ trở thành cơn ác mộng bảo trì khi API của bạn thay đổi. Sẽ được ưu tiên hơn nhiều khi có các rangetham số truy vấn hoặc tải trọng được chỉ định là phần chính của câu hỏi này: để hiểu cách tiếp cận thực tiễn tốt nhất cho vấn đề mà tôi muốn nói không phải là điều này.
digitaldreamer

@digitaldreamer - Tôi không hiểu ý bạn về cấu trúc URI phức tạp? Ngoài ra, đây là HTTP DELETE nên tải trọng không phải là một tùy chọn mà là các tham số truy vấn có.
Suresh Kumar
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.