web-dev-qa-db-ja.com

RESTfulundelete

データサービスの削除解除または遅延/バッチ削除をサポートすることは、かなり一般的な要件です。私が疑問に思っているのは、これをRESTfulな方法で実装する方法です。私はいくつかの異なるオプションの間で引き裂かれています(どれも私にとってひどく魅力的ではないようです)。これらのさまざまなオプションに共通しているのは、特定のリソースタイプに対して削除済みとしてマークされたすべてのリソースを返すAPIの必要性です。

これが私が考えたいくつかのオプションとそれらの賛否両論のいくつかです:

リソースを削除済みとしてマークするオプション:

  • HTTP DELETEを使用して、リソースを削除済みとしてマークします。
  • HTTP PUT/POSTを使用して、削除されたフラグを更新します。これは、本質的に削除であるものをHTTP DELETEメソッドから他のHTTPメソッドにマップするため、正しくありません。

削除のマークが付けられたリソースをGETするときのオプション:

  • 削除済みとしてマークされたリソースのHTTPステータス404を返します。クリーンで透過的ですが、実際に削除されたリソースと削除済みとしてマークされたリソースの違いをどのように見分けるのですか。
  • HTTPステータス410を返します。違いを伝える方法を提供しますが、410は技術的には「永続的と見なされることが期待されます。リンク編集機能を持つクライアントは、ユーザーの承認後にRequest-URIへの参照を削除する必要があります」と述べています。ここで「期待される」と「すべきである」という言葉には十分な揺れの余地があるかもしれません。クライアントで410がどの程度サポートされているか/理解されているかわからない。
  • HTTPステータス200を返し、リソースが削除されたことを示すフラグフィールドを含めます。そもそも削除するという考えは、実際には表示されたくないためだったので、これは奇妙に思えます。これにより、削除されたリソースを除外する責任がクライアントに押し下げられます。

この削除されたリソースを含む応答のオプション:

  • 削除済みとして作成されたリソースを省略します。清潔でシンプル。しかし、削除されたリソースについて実際に知りたい場合はどうでしょうか。
  • それらが削除されたことを示すフィールドと一緒にそれらを含めます。これにより、削除されたリソースを除外する責任がクライアントに押し下げられます。アクティブなリソースまたは削除されたリソースのみをページングする場合は、ページ付けが難しくなります。

削除のマークが付けられたリソースを更新するときのオプション:

  • HTTPステータス404を使用します。リソースが正しくなくなっていますか?しかし、削除済みとしてマークされたリソースと実際に削除されたリソースの違いをどのように見分けることができますか。 404応答のHTTPボディはここで明確にすることができますが、クライアントは明確にするためにボディを解析/解釈する必要があります。たぶん、応答ヘッダーがここで役立つかもしれませんか?どれ?カスタムヘッダー?
  • リソースを最初に削除解除する方法に関するメッセージとともにHTTPステータス409を使用します。

削除対象としてマークされたリソースの削除を取り消すオプション:

  • リソースの更新操作にはHTTPPUT/POSTを使用し、再度アクティブとしてマークします。これは、リソースのGET操作に対してHTTP 404を返さない場合にのみ機能します。これは、「見つからない」(404)リソースへのPUT/POST以降は行われないためです。
  • リソースの作成操作にはHTTPPUT/POSTを使用します。ここでの問題は、どのデータが優先されるかということです。作成操作で送信されたデータ?または、削除されていないデータですか?それを返す他のクエリからそれを除外します。次に、リソースIDが削除済みとしてマークされたリソースを指している場合、リソースを作成するHTTP PUT/POSTを削除解除として扱います。
  • 個別のREST削除対象としてマークされたリソースの削除解除専用のパス。

これは完全なリストではありません。頭の中で跳ね回っているオプションのいくつかを列挙したかっただけです。

これを行う方法の答えは、いつものように「状況によって異なります」です。私が興味を持っているのは、決定を下すためにどのような資格/要件を使用するかということです。これが自分で実装または実装されているのをどのように見ましたか?

56
Caleb

本で行く: 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を使用してリソースを更新してください。

8
jmkeyes

これを解決する最もRESTfulな方法は、HTTP PUTを使用してリソースに削除(および削除の取り消し)のマークを付けてから、HTTPDELETEを使用してリソースを完全に削除することだと思います。削除のマークが付けられたリソースのリストを取得するには、HTTPGETリクエストでパラメータを使用します。 ?state=markedForDeletion。パラメータなしで削除のマークが付けられたリソースをリクエストした場合、「404NotFound」ステータスを返す必要があると思います。

4
Espen Burud

ショートバージョン

元の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インターフェースの外でそれを行わなければならないということです。

3
John B

「削除された」(ゴミ箱に入れられた)アイテムもリソースと見なされる可能性がありますよね?次に、次のいずれかの方法でこのリソースにアクセスできます(たとえば、削除されたユーザーの場合)。

PATCH deleted_users/{id}
PATCH trash/users/{id}
PATCH deleted/users/{id}

または、これがより安らかな方法であると考える人もいるかもしれません。

PATCH deleted/{id}?type=users

ペイロードでは次のようになります。

{ deleted_at: null }
2

私もこの問題を抱えており、インターネットで最善の解決策を探しています。私が見つけることができる主な答えのどれも私には正しくないように思われるので、ここに私自身の研究結果があります。

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フィールドを変更するか、移動などの複雑な操作を実行することです。そうしないと、GETPOSTDELETE(これは、DELETEが即時であり、エラーが発生して部分的な移動が発生する可能性があることを前提としています...) PATCHは、任意の数のメソッドを持つことに似ています。 UNDELETEまたはMOVEメソッドも同様に機能しますが、RFCには、 標準化されたメソッド のセットがあると明確に記載されているため、これらとPATCHは、独自のメソッドを追加する必要がない十分なスペースを提供します。仕様には、独自のメソッドを追加すべきではないと言っているものは何もありませんでした。ただし、そうする場合は、それらを明確に文書化してください。

1
Alexis Wilke

モデルの作成を強制しました

POST/modelname /:id/undelete

0
realtebo