web-dev-qa-db-ja.com

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

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

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

明らかに、誰にもPUTを返してほしくない

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

そしてそれを成功させる。実際、おそらくimplementにすら行かないでしょう。この場合、それが成功する可能性さえあります。

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

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

黙って捨てるべきですか?

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

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

50
Robin Green

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

それらが similar であるといいのですが、PUTが少し異なる動作をするのは珍しいことではありません。たとえば、便宜上、GETによって返されるコンテンツになんらかのIDが含まれている多くのAPIを見てきました。ただし、PUTの場合、そのIDはURIによってのみ決定され、コンテンツでは意味がありません。本文で見つかったすべてのIDは黙って無視されます。

RESTとWebは一般に 堅牢性の原則 に大きく結びついています: "あなたが行うことを保守的にし、[受け入れる]ものを寛容にしてください。" これに哲学的に同意する場合、解決策は明白です:PUTリクエスト内の無効なデータを無視します。これは、例のように不変データと実際のナンセンス(例:不明なフィールド。

PATCHは別のオプションである可能性がありますが、実際に部分更新をサポートするのでない限り、PATCHを実装しないでください。 PATCHは、コンテンツに含める特定の属性のみを更新することを意味します; ではないは、エンティティ全体を置き換えることを意味しますが、特定のフィールドを除外します。あなたが実際に話しているのは、実際には部分的な更新ではなく、完全な更新であり、べき等であり、リソースのその部分が読み取り専用であるだけです。

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

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

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

GETを実行する前に現在のデータを取得するためにクライアントが常にPUTを実行する必要があるため、409での等式チェックは本当に嫌いです。それはいいことではなく、おそらくどこかでパフォーマンスの低下につながるでしょう。また、 really 403(Forbidden)は好きではありません。これは、 entire リソースが、それ。だから私の意見では、堅牢性の原則に従うのではなく絶対に検証する必要がある場合は、リクエストの all を検証し、追加のフィールドまたは書き込み不可能なフィールドがある場合は400を返します。

400/409 /何でも、特定の問題とは何か、それを修正する方法についての情報が含まれていることを確認してください。

これらのアプローチはどちらも有効ですが、私はロバストネスの原則に沿って、前者のアプローチを優先します。大規模なREST APIを使用した /での作業を経験したことがある場合は、下位互換性の価値に感謝します。既存のフィールドまたはそれを読み取り専用にすると、サーバーがそれらのフィールドを無視するだけの場合は下位互換性があり、古いクライアントは引き続き機能します。ただし、コンテンツに対して厳密な検証を行うと、後方互換性がなくなり、古いクライアントは機能しなくなります。前者は一般に、APIのメンテナーとそのクライアントの両方の作業が少なくなることを意味します。

51
Aaronaught

Idem Potency

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

部分的な更新を許可した場合、それはもはやべき等になることはできません。 2つのクライアントがある場合。クライアントAおよびBの場合、次のシナリオが発展する可能性があります。

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

これにより不整合が生じます。画像に誤ったメタデータが添付されています!

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

PUTの意味は変更できません(ただし、誤って使用することはできます)。

その他のオプション

幸いにも別のオプションがあります。これはパッチです。 PATCHは、構造を部分的に更新できる方法です。単純に部分構造を送信できます。単純なアプリケーションの場合、これで十分です。この方法は、idemの有効性が保証されていません。クライアントは次の形式でリクエストを送信する必要があります。

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を選択できます。更新プロセスの見方によって異なります。これを一連のルール(システム中心)として見ると、競合がより適切になります。これらのフィールドは更新できません。 (規則と矛盾しています)。承認の問題(ユーザー中心)であると思われる場合は、禁じられた状態に戻してください。あり:これらのフィールドを変更する権限がありません。

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

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

個人的な意見

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

application/json-patch

そしてjson:application/json-patch

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

10
Edgar Klerks