REST APIは、部分的に変更可能なリソースへのPUTリクエストをどのように処理する必要がありますか?


46

HTTP GETリクエストに応答して、REST API がサブオブジェクトで追加データを返すと仮定しますowner

{
  id: 'xyz',
  ... some other data ...
  owner: {
    name: 'Jo Bloggs',
    role: 'Programmer'
  }
}

明らかに、誰もがPUTバックアップできるようにしたくない

{
  id: 'xyz',
  ... some other data ...
  owner: {
    name: 'Jo Bloggs',
    role: 'CEO'
  }
}

そしてそれを成功させます。確かに、この場合、おそらく潜在的に成功する方法さえ実装しないでしょう。

しかし、この質問はサブオブジェクトだけではありません。一般的に、PUTリクエストで変更できないデータを使用して何を行う必要がありますか?

PUTリクエストから欠落している必要がありますか?

静かに捨てるべきですか?

チェックする必要があり、その属性の古い値と異なる場合、応答でHTTPエラーコードを返しますか?

または、JSON全体を送信するのではなく、RFC 6902 JSONパッチを使用する必要がありますか?


2
これらはすべて機能します。それはあなたの要件に依存すると思います。
ロバートハーベイ

最も驚きの原則は、PUTリクエストからそれが欠落しているべきであることを示していると思います。それが可能でない場合は、それが異なるかどうかを確認し、エラーコードを返します。サイレント破棄は最悪です(ユーザー送信は、それが変更されることを期待しており、「200 OK」と伝えます)。
マチェイピエチョトカ

2
@MaciejPiechotkaそれに関する問題は、挿入または取得などと同じモデルをプットで使用できないことです。同じモデルが使用されることを望み、フィールド認可ルールがあるので、値を入力する場合変更しないフィールド、403 Forbiddenを返します。後で許可が許可されるように設定されている場合、許可されていない場合は401 Unauthorizedを取得します
Jimmy Hoffa

@JimmyHoffa:モデルとは、データ形式を意味します(選択に応じてMVC Restフレームワークでモデルを再利用できる場合があります。使用されている場合、OPは言及していません)。フレームワークによる制約がなく、早期エラーの方が変更のチェックより実装が簡単で発見/容易である場合、発見可能性があります(OK-フィールドXYZには触れないでください)。いずれにせよ、廃棄は最悪です。
マチェイピエチョトカ

回答:


46

W3C仕様にもRESTの非公式のルールにPUTも、対応するものと同じスキーマ/モデルを使用する必要があるというルールはありませんGET

それらが似ていればいいのですが、PUT少し違うことをするのは珍しいことではありません。たとえばGET、便宜上、によって返されるコンテンツに何らかのIDを含むAPIをたくさん見ました。ただし、を使用するとPUT、そのIDはURIによって排他的に決定され、コンテンツでは意味を持ちません。本文で見つかったIDは黙って無視されます。

RESTおよび一般的なウェブは重くに結びついている堅牢性の原則「あなたが受け入れるものにリベラルこと、[送信]あなたは何をすべきかで保守的です。」哲学的にこれに同意する場合、解決策は明白ですPUT。リクエスト内の無効なデータを無視します。これは、例のように不変のデータと、未知のフィールドなどの実際のナンセンスの両方に適用されます。

PATCHは別のオプションである可能性がありますが、PATCH実際には部分的な更新をサポートする場合以外は実装しないでください。PATCH、コンテンツに含める特定の属性のみを更新することを意味します。それは全体の実体を取り替えることを意味しませが、いくつかの特定の分野を排除します。あなたが実際に話しているのは、実際には部分的な更新ではなく、完全な更新であり、べき等であり、リソースのその部分だけが読み取り専用です。

このオプションを選択した場合の良いことは、応答で実際に更新されたエンティティで200(OK)を送り返すことです。これにより、クライアントは読み取り専用フィールドが更新されなかったことを明確に確認できます。

確かにあります。何人かの人々、他の方法を考える-リソースの読み取り専用の部分を更新しようとするエラーでなければならないこと。これには、主に、リソース全体が読み取り専用で、ユーザーが更新しようとした場合に間違いなくエラーを返すという根拠に基づいています。堅牢性の原則に反することは間違いありませんが、APIのユーザーにとっては、より「自己文書化」されると考えるかもしれません。

これには2つの規則があり、どちらも元のアイデアに対応しますが、それらを拡張します。1つ目は、読み取り専用フィールドがコンテンツに表示されないようにし、表示された場合はHTTP 400(Bad Request)を返すことです。この種のAPIは、他の認識できない/使用できないフィールドがある場合、HTTP 400も返す必要があります。2つ目は、読み取り専用フィールドが現在のコンテンツと同一であることを要求し、値が一致しない場合は409(競合)を返すことです。

GET使用する前に現在のデータを取得するためにクライアントが常にaを実行する必要があるため、409での等価性チェックは本当に嫌いPUTです。それは単にいいことではなく、おそらく誰かのために、どこかでパフォーマンスの低下につながるでしょう。またリソースの一部だけではなく、リソース全体が保護されていることを意味するため、403(禁止)はあまり好きでありません。したがって、私の意見では、堅牢性の原則に従う代わりに絶対に検証する必要がある場合、すべてのリクエストを検証、追加または書き込み不可のフィールドがある場合は400を返します。

400/409 / whateverに特定の問題とその修正方法に関する情報が含まれていることを確認してください。

これらのアプローチはどちらも有効ですが、堅牢性の原則に沿って、前者のアプローチを好みます。大規模なREST APIを使用た経験がある場合は、下位互換性の価値を高く評価するでしょう。既存のフィールドを削除するか、読み取り専用にすることを決定した場合、サーバーがそれらのフィールドを無視するだけであれば、下位互換性のある変更となり、古いクライアントは引き続き動作します。あなたがコンテンツに厳密な検証を行う場合は、それがないもはや下位互換性、および古いクライアントは機能しなくなります。前者は通常、APIのメンテナーとそのクライアントの両方の作業量が少ないことを意味します。


1
良い答えであり、賛成です。ただし、既存のフィールドを削除するか、読み取り専用にすることを決定した場合、サーバーがそれらのフィールドを無視するだけであれば、下位互換性のある変更であり、古いクライアントは引き続き動作します。 」クライアントがこの削除された/新しく読み取り専用のフィールドに依存している場合、これはまだアプリの全体的な動作に影響しませんか?フィールドを削除する場合、データを無視するよりも、明示的にエラーを生成する方がおそらく良いと思います。そうでない場合、クライアントは、以前に機能していた更新が現在失敗していることを知りません。
rinogo

この答えは間違っています。RFC2616の2つの理由から:1.(セクション9.1.2)PUTは独立している必要があります。何回も置くと、1回だけ置くのと同じ結果になります。2.リソースへのgetは、リソースを変更するために他の要求が行われなかった場合、エンティティputを返す必要があります
-brunoais

1
不変値がリクエストで送信された場合にのみ同等性チェックを行うとどうなりますか。これにより、2つの世界のベストが得られると思います。クライアントにGETを強制することはありませんが、不変の値が無効であると送信した場合は、何かがおかしいと通知します。
アフマドアブデルガニー

おかげで、経験から来た最後の段落で行った詳細な比較は、まさに私が探していたものです。
dhill

9

同一効力

RFCに従って、PUTは完全なオブジェクトをリソースに配信する必要があります。この主な理由は、PUTがべき等であるべきだということです。これは、サーバー上で同じ結果を評価するために繰り返される要求を意味します。

部分的な更新を許可する場合、それはもはや同効ではありません。2つのクライアントがある場合。クライアントAとBの場合、次のシナリオが進化する可能性があります。

クライアントAは、リソースイメージから画像を取得します。これには、画像の説明が含まれていますが、これはまだ有効です。クライアントBは新しいイメージを配置し、それに応じて説明を更新します。画像が変更されました。クライアントAは、説明を変更する必要がないことを確認しました。これは、希望どおりであり、画像のみを配置するためです。

これにより矛盾が発生し、画像に間違ったメタデータが添付されます!

さらに厄介なのは、仲介者がリクエストを繰り返すことができることです。何らかの理由でPUTが失敗したと判断した場合。

PUTの意味は変更できません(誤用することはできますが)。

別のオプション

幸い、別のオプションがあります。これはPATCHです。PATCHは、構造を部分的に更新できるメソッドです。部分的な構造を送信するだけです。単純なアプリケーションの場合、これで問題ありません。この方法は、強力であるとは限りません。クライアントは、次の形式でリクエストを送信する必要があります。

PATCH /file.txt HTTP/1.1
Host: www.example.com
Content-Type: application/example
If-Match: "e0023aa4e"
Content-Length: 20
{fielda: 1, fieldc: 2}

また、サーバーは204(コンテンツなし)で返信して成功を示すことができます。エラー時には、構造の一部を更新できません。PATCHメソッドはアトミックです。

この方法の欠点は、すべてのブラウザーがこれをサポートしているわけではないことですが、これはRESTサービスの最も自然なオプションです。

パッチリクエストの例: http : //tools.ietf.org/html/rfc5789#section-2.1

JSONパッチ

jsonオプションはかなり包括的で興味深いオプションのようです。ただし、サードパーティ向けに実装するのは難しい場合があります。ユーザーベースがこれを処理できるかどうかを判断する必要があります。

また、コマンドを部分構造に変換する小さなインタープリターを作成する必要があるため、モデルを更新するために使用するため、多少複雑です。このインタープリターは、提供されたコマンドが意味をなすかどうかもチェックする必要があります。一部のコマンドは互いにキャンセルします。(fieldaの書き込み、fieldaの削除)。これをクライアントに報告して、クライアント側のデバッグ時間を制限したいと思います。

しかし、時間があれば、これは本当にエレガントなソリューションです。もちろん、フィールドを検証する必要があります。これをPATCHメソッドと組み合わせて、RESTモデルにとどめることができます。しかし、ここではPOSTは受け入れられると思います。

悪くなる

PUTオプションを使用することにした場合、これは多少危険です。その後、少なくともエラーを破棄しないでください。ユーザーには一定の期待値があり(データが更新されます)、これを破ると、一部の開発者に良い時間を与えないことになります。

フラグを立てるには、409 Conflictまたは403 Forbiddenを選択できます。更新プロセスの見方によって異なります。ルールのセット(システム中心)として見ると、競合はより良くなります。これらのフィールドは更新できません。(ルールと矛盾します)。承認の問題(ユーザー中心)として見られる場合は、forbiddenを返す必要があります。With:これらのフィールドを変更する権限がありません。

ユーザーにすべての変更可能なフィールドを送信するように強制する必要があります。

これを強制する合理的なオプションは、変更可能なデータのみを提供するサブリソースに設定することです。

個人的な意見

個人的には、ブラウザで作業する必要がない場合は単純なPATCHモデルに行き、その後JSONパッチプロセッサで拡張します。これは、MIMEタイプを区別することで実行できます。JSONパッチのMIMEタイプ:

application / json-patch

そしてjson:application / json-patch

2つのフェーズで簡単に実装できます。


3
あなたのdem等性の例は意味がありません。説明を変更するか、変更しません。いずれにしても、毎回同じ結果が得られます。
ロバートハーベイ

1
あなたは正しい、私はそれが寝る時間だと思う。編集できません。これは、PUT要求ですべてのデータを送信する合理性に関する例です。ポインターをありがとう。
エドガークラーク

私はこれが3年前であることを知っています...しかし、RFCのどこで「PUTがリソースに完全なオブジェクトを配信しなければならないか」に関する詳細情報を見つけることができることを知っていますか。私はこれを他の場所で言及しましたが、仕様でどのように定義されているかを見たいです。
CSharper

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.