データサービスの削除解除または遅延/バッチ削除をサポートすることは、かなり一般的な要件です。私が疑問に思っているのは、これをRESTfulな方法で実装する方法です。私はいくつかの異なるオプションの間で引き裂かれています(どれも私にとってひどく魅力的ではないようです)。これらのさまざまなオプションに共通しているのは、特定のリソースタイプに対して削除済みとしてマークされたすべてのリソースを返すAPIの必要性です。
これが私が考えたいくつかのオプションとそれらの賛否両論のいくつかです:
リソースを削除済みとしてマークするオプション:
削除のマークが付けられたリソースをGETするときのオプション:
この削除されたリソースを含む応答のオプション:
削除のマークが付けられたリソースを更新するときのオプション:
削除対象としてマークされたリソースの削除を取り消すオプション:
これは完全なリストではありません。頭の中で跳ね回っているオプションのいくつかを列挙したかっただけです。
これを行う方法の答えは、いつものように「状況によって異なります」です。私が興味を持っているのは、決定を下すためにどのような資格/要件を使用するかということです。これが自分で実装または実装されているのをどのように見ましたか?
本で行く: RFC 2616-9.7 :
The DELETE method requests that the Origin server delete the resource
identified by the Request-URI. This method MAY be overridden by human
intervention (or other means) on the Origin server. The client cannot
be guaranteed that the operation has been carried out, even if the
status code returned from the Origin server indicates that the action
has been completed successfully. However, the server SHOULD NOT
indicate success unless, at the time the response is given, if it intends
to delete the resource or move it to an inaccessible location.
リソースを削除する場合、サーバーはその側でリソースを削除対象としてマークする必要があります。リソースを実際に削除する必要はありません。操作が実行されたことを保証することはできません。それでも、サーバーは、削除されていないのに削除されたと言ってはなりません。
A successful response SHOULD be 200 (OK) if the response includes an entity
describing the status, 202 (Accepted) if the action has not yet been enacted,
or 204 (No Content) if the action has been enacted but the response does not
include an entity.
操作が遅れる場合は、アクションの結果を説明する202とエンティティ本体を送信します。 (サーバーによるリソースの延期された削除を表すポーリング可能な「タスク」を考えてみてください。理論的には、リソースをその状態のままにしておくことができます。)クライアントが取得できないようにするだけです。再び元の形式になります。応答コードに410を使用し、「タスク」が終了するか、サーバーがリソースを削除する場合は、404を返します。
ただし、DELETEのセマンティクスが問題のリソースにとって意味をなさない場合、おそらくそれは探している削除ではなく、リソースの状態を変更するがアクセス可能な状態を維持する追加の状態遷移ですか?その場合は、PUT/PATCHを使用してリソースを更新してください。
これを解決する最もRESTfulな方法は、HTTP PUTを使用してリソースに削除(および削除の取り消し)のマークを付けてから、HTTPDELETEを使用してリソースを完全に削除することだと思います。削除のマークが付けられたリソースのリストを取得するには、HTTPGETリクエストでパラメータを使用します。 ?state=markedForDeletion
。パラメータなしで削除のマークが付けられたリソースをリクエストした場合、「404NotFound」ステータスを返す必要があると思います。
ショートバージョン
元のURIのメソッドを使用してリソースをRESTfulに元に戻すことはできません-削除されたリソースで試行された操作は404または410を返す必要があるため、非論理的です。これは仕様に明示的に記載されていませんが、強く暗示されていますDELETEメソッドの定義 1 (強調を追加):
事実上、このメソッドはUNIXのrmコマンドに似ています。これは、以前に関連付けられた情報が削除されることを期待するのではなく、OriginサーバーのRIマッピングの削除操作を表します。
つまり、リソースを削除すると、サーバーはそのURIをそのデータにマップしなくなります。したがって、PUTまたはPOSTを「これを削除されていないものとしてマークする」などの更新を行うことはできません(リソースはURIといくつかの基になるデータ間のマッピングとして定義されていることに注意してください) 。
いくつかの解決策
基になるデータが必ずしも削除されるとは限らないと明示的に述べられているため、サーバーがDELETE実装の一部としてnewURIマッピングを作成することを妨げず、それによってバックアップコピーを効果的に作成します。後で復元できます。
削除されたすべてのアイテムを含む「/ deleted /」コレクションを持つことができますが、実際にどのように元に戻すのですか?おそらく最も簡単なRESTfulな方法は、クライアントにGETを使用してアイテムを取得させ、次にPOST目的のURLにアイテムを取得させることです。
削除したアイテムを元の場所に復元できるようにする必要がある場合はどうなりますか?それをサポートするメディアタイプを使用している場合は、/ deleted /コレクションからのGETへの応答に元のURIを含めることができます。その後、クライアントはそれを使用してPOSTを実行できます。このような応答は、JSONでは次のようになります。
{
"original-url":"/some/place/this/was/deleted/from",
"body":<base64 encoded body>
}
クライアントは、その本体をそのURIにPOSTして、削除を取り消すことができます。
または、リソース定義で移動の概念が許可されている場合(「location」プロパティなどを更新することにより)、部分的な更新を実行して、オブジェクト全体のラウンドトリップを回避できます。または、ほとんどの人が行うことを実行し、RPCのような操作を実装して、サーバーにリソースを移動するように指示します。 UnRESTful、はい、しかしそれはおそらくほとんどの状況でうまくいくでしょう。
これらのことをどのように決定するか
これらのことをどのように決定するかという問題に関して:アプリケーションのコンテキストで削除が何を意味するのか、そしてなぜそれが必要なのかを考慮する必要があります。多くのアプリケーションでは、何も削除されません。「削除」とは、実際には「明示的に削除を取り消さない限り、このアイテムを以降のすべてのクエリ/リストなどから除外する」ことを意味します。つまり、これは実際には単なるメタデータ、つまり移動操作です。その場合、なぜHTTPDELETEを気にするのですか?理由の1つは、2層の削除が必要な場合です。つまり、元に戻すことができるソフトバージョンまたは一時バージョンと、そうではないハード/永続バージョンです。
特定のアプリケーションコンテキストがない場合は、次のように実装する傾向があります。
便宜上、このリソースはもう表示したくありません:部分的な更新をPOSTして、リソースを「一時的に削除された」としてマークします
恥ずかしい/罪を犯す/お金がかかるなどの理由で、このリソースに誰も到達できないようにしたくない:HTTP DELETE
考慮すべき次の質問は、永久削除はURIを永久にマップ解除するだけで、誰もURIにリンクできないようにする必要があるのか、それとも基になるデータもパージする必要があるのかということです。明らかに、データを保持している場合、管理者は「完全に」削除されたリソースでさえ復元できます(ただし、RESTfulインターフェイスを介さないでください)。これの欠点は、データの所有者が本当にデータをパージしたい場合、管理者はRESTインターフェースの外でそれを行わなければならないということです。
「削除された」(ゴミ箱に入れられた)アイテムもリソースと見なされる可能性がありますよね?次に、次のいずれかの方法でこのリソースにアクセスできます(たとえば、削除されたユーザーの場合)。
PATCH deleted_users/{id}
PATCH trash/users/{id}
PATCH deleted/users/{id}
または、これがより安らかな方法であると考える人もいるかもしれません。
PATCH deleted/{id}?type=users
ペイロードでは次のようになります。
{ deleted_at: null }
私もこの問題を抱えており、インターネットで最善の解決策を探しています。私が見つけることができる主な答えのどれも私には正しくないように思われるので、ここに私自身の研究結果があります。
DELETE
が進むべき道であるという人もいます。フラグを含めて、それがすぐに永続的なDELETE
であるか、ゴミ箱に移動するかを決定できます(おそらく、管理者だけが即時の永続的なDELETE
を実行できます)。
DELETE /api/1/book/33
DELETE /api/1/book/33?permanent
その後、バックエンドは本を削除済みとしてマークできます。 SQLデータベースがあるとすると、次のようになります。
UPDATE books SET status = 'deleted' WHERE book_id = 33;
他の人が述べたように、DELETE
が完了すると、コレクションのGET
はそのアイテムを返しません。 SQLに関しては、これは、ステータスがdeleted
のアイテムを返さないようにする必要があることを意味します。
SELECT * FROM books WHERE status <> 'deleted';
また、GET /api/1/book/33
を実行するときは、404または410を返す必要があります。410の問題の1つは、GoneForever(at少なくともそれはそのエラーコードの私の理解です)、したがって、アイテムが存在する限りは404を返しますが、'deleted'
および410としてマークされています完全に削除された後。
削除を取り消すための正しい方法は、PATCH
です。アイテムの更新に使用されるPUT
とは異なり、PATCH
はアイテムに対する操作であることが期待されます。私が見ることができることから、操作はペイロードにあると予想されます。それが機能するためには、リソースが何らかの方法でアクセス可能である必要があります。他の誰かが提案したように、削除すると本が表示されるtrashcan
領域を指定できます。このようなものは、ゴミ箱に入れられた本を一覧表示するのに役立ちます。
GET /api/1/trashcan/books
[{"path":"/api/1/trashcan/books/33"}]
したがって、結果のリストには本番号33が含まれるようになり、次のような操作でPATCH
を実行できます。
PATCH /api/1/trashcan/books/33
{
"operation": "undelete"
}
操作をより用途の広いものにしたい場合は、次のようなものを使用できます。
PATCH /api/1/trashcan/books/33
{
"operation": "move",
"new-path": "/api/1/books/33"
}
次に、「move」は、インターフェースで可能な限り、URLの他の変更に使用できます。 (私は、ページへのパスがtree
という1つのテーブルにあり、各ページがpage
という別のテーブルにあり、識別子を持っているCMSに取り組んでいます。パスを変更できます。 tree
テーブルのパス間でページを移動することでページを移動できます!ここでPATCH
が非常に役立ちます。)
残念ながら、RFCはPATCH
を明確に定義しておらず、新しいバージョンを表すペイロードを受け入れるPUT
とは対照的に、上記のような操作で使用されるだけです。 、対象アイテムの:
PUT /api/1/books/33
{
"title": "New Title Here"
}
一方、対応するPATCH
(両方をサポートする場合)は次のようになります。
PATCH /api/1/books/33
{
"operation": "replace",
"field": "title",
"value": "New Title Here"
}
その多くのPATCH
操作をサポートするのはおかしいと思います。しかし、いくつかの良い例は、なぜPATCH
が正しい解決策であるかについてのより良い考えを与えると思います。
パッチの使用は、virtualフィールドを変更するか、移動などの複雑な操作を実行することです。そうしないと、GET
、POST
、DELETE
(これは、DELETE
が即時であり、エラーが発生して部分的な移動が発生する可能性があることを前提としています...) PATCH
は、任意の数のメソッドを持つことに似ています。 UNDELETE
またはMOVE
メソッドも同様に機能しますが、RFCには、 標準化されたメソッド のセットがあると明確に記載されているため、これらとPATCH
は、独自のメソッドを追加する必要がない十分なスペースを提供します。仕様には、独自のメソッドを追加すべきではないと言っているものは何もありませんでした。ただし、そうする場合は、それらを明確に文書化してください。
モデルの作成を強制しました
POST/modelname /:id/undelete