REST APIの実際のシナリオでのPUTメソッドとPATCHメソッドの使用


680

まず、いくつかの定義:

PUTはセクション9.6 RFC 2616で定義されています

PUTメソッドは、囲まれたエンティティが指定されたRequest-URIの下に格納されることを要求します。Request-URIが既存のリソースを参照している場合、囲まれたエンティティは、元のサーバーにあるエンティティの変更バージョンと見なされるべきです(SHOULD)。Request-URIが既存のリソースを指さず、そのURIが要求元のユーザーエージェントによって新しいリソースとして定義できる場合、オリジンサーバーはそのURIでリソースを作成できます。

PATCHはRFC 5789で定義されています

PATCHメソッドは、要求エンティティに記述されている一連の変更を、Request-URIで識別されるリソースに適用するように要求します。

また、RFC 2616セクション9.1.2によれば、PUTはべき等ではなく、PUTはべき等です。

次に、実際の例を見てみましょう。/usersデータを使用してPOSTを実行し、{username: 'skwee357', email: 'skwee357@domain.com'}サーバーがリソースを作成できる場合、サーバーは201とリソースの場所で応答し(仮定/users/1)、次のGET呼び出し/users/1はを返し{id: 1, username: 'skwee357', email: 'skwee357@domain.com'}ます。

次に、メールを変更したいとします。メールの変更は「一連の変更」と見なされるため/users/1、「パッチドキュメント」でパッチを適用する必要があります。私のケースでは、JSON形式のドキュメントのようになります。{email: 'skwee357@newdomain.com'}。その後、サーバーは200を返します(許可に問題がない場合)。これは私に最初の質問をもたらします:

  • パッチはべき等ではありません。RFC 2616とRFC 5789でそう述べられています。ただし、同じPATCHリクエストを(新しいメールで)発行すると、同じリソースの状態が取得されます(メールはリクエストされた値に変更されます)。なぜPATCHはべき等ではないのですか?

PATCHは比較的新しい動詞(2010年3月に導入されたRFC)であり、一連のフィールドの「パッチ」または変更の問題を解決するようになります。PATCHが導入される前は、誰もがPUTを使用してリソースを更新していました。しかし、PATCHが導入された後は、PUTが何に使用されるのか混乱します。そして、これは私に私の2番目の(そして主要な)質問をもたらします:

  • PUTとPATCHの本当の違いは何ですか?特定のリソースの下のエンティティ全体を置き換えるために PUTが使用される可能性があることをどこかで読んだので、(PATCHのような属性のセットの代わりに)エンティティ全体を送信する必要があります。そのような場合の実際の使用法は何ですか?特定のリソースURIにあるエンティティをいつ置換または上書きしますか。また、そのような操作がエンティティの更新/パッチ適用と見なされないのはなぜですか?PUTで私が目にする唯一の実用的な使用例は、コレクションに対してPUTを発行すること、つまり/usersコレクション全体を置き換えることです。PATCHが導入された後、特定のエンティティでPUTを発行しても意味がありません。私が間違っている?

1
a)2612ではなくRFC 2616です。b)RFC 2616は廃止されました。PUTの現在の仕様はgreenbytes.de/tech/webdav/rfc7231.html#PUTにあります。c)質問はありません。PUTを使用して、コレクションだけでなく任意のリソースを置き換えることができることはかなり明白ではありません。d)PATCHが導入される前、人々は通常POSTを使用していました。e)最後に、特定の PATCHリクエスト(パッチ形式によって異なります)べき等にすることできます。それは一般的ではないということだけです。
Julian Reschke 2015

それは場合に役立ちます私は、PUTの対PATCHの記事書いてきましたeq8.eu/blogs/36-patch-vs-put-and-the-patch-json-syntax-war
equivalent8

5
シンプル:POSTはコレクション内にアイテムを作成します。PUTはアイテムを置き換えます。PATCHはアイテムを変更します。POSTを実行すると、新しいアイテムのURLが計算されて応答で返されますが、PUTおよびPATCHでは要求にURLが必要です。正しい?
トムラッセル

この投稿は役に立つかもしれません:POST vs PUT vs PATCH: mscharhag.com/api-design/http-post-put-patch
micha

回答:


941

:最初にRESTについて読むことに時間を費やしたとき、べき等性は正しく理解しようとする混乱する概念でした。追加のコメント(およびJason Hoetgerの回答)が示すように、私はまだ元の回答では完全に正しく理解していませんでした。しばらくの間、Jasonの効果的な盗用を避けるために、この回答を大幅に更新することに抵抗してきましたが、(コメントで)求められたため、現在編集しています。

私の回答を読んだ後、この質問に対するJason Hoetgerの優れた回答も読むことをお勧めします。Jasonから単に盗むことなく、私の回答をより良くするように努めます。

なぜPUTはべき等なのですか?

RFC 2616の引用で述べたように、PUTはべき等と見なされます。リソースをPUTする場合、次の2つの前提が機能します。

  1. コレクションではなくエンティティを参照しています。

  2. 指定するエンティティは完全です(エンティティ全体)。

例の1つを見てみましょう。

{ "username": "skwee357", "email": "skwee357@domain.com" }

このドキュメントをPOSTした場合 /usersにすると、提案どおり、次のようなエンティティが返される場合があります。

## /users/1

{
    "username": "skwee357",
    "email": "skwee357@domain.com"
}

このエンティティを後で変更する場合は、PUTまたはPATCHを選択します。PUTは次のようになります。

PUT /users/1
{
    "username": "skwee357",
    "email": "skwee357@gmail.com"       // new email address
}

PATCHを使用して同じことを達成できます。次のようになります。

PATCH /users/1
{
    "email": "skwee357@gmail.com"       // new email address
}

これら2つの違いはすぐにわかります。PUTにはこのユーザーのすべてのパラメーターが含まれていましたが、PATCHには変更中のパラメーターのみが含まれていました(email)。

PUTを使用する場合、完全なエンティティを送信していること、およびその完全なエンティティを想定しています。 そのURIにある既存のエンティティを置き換えます。上記の例では、PUTとPATCHは同じ目標を達成します。どちらもこのユーザーの電子メールアドレスを変更します。ただし、PUTCHはエンティティ全体を置き換えることで処理しますが、PATCHは提供されたフィールドのみを更新し、その他はそのままにします。

PUTリクエストにはエンティティ全体が含まれるため、同じリクエストを繰り返し発行すると、常に同じ結果になるはずです(送信したデータはエンティティのデータ全体になります)。したがって、PUTはべき等です。

間違ったPUTの使用

上記のPATCHデータをPUTリクエストで使用するとどうなりますか?

GET /users/1
{
    "username": "skwee357",
    "email": "skwee357@domain.com"
}
PUT /users/1
{
    "email": "skwee357@gmail.com"       // new email address
}

GET /users/1
{
    "email": "skwee357@gmail.com"      // new email address... and nothing else!
}

(私はこの質問の目的のために、サーバーには特定の必須フィールドがなく、これが発生することを許可すると想定しています...実際にはそうではないかもしれません。)

PUTを使用しましたが、のみが提供されているためemail、このエンティティではこれだけです。これによりデータが失われました。

この例は、説明の目的でここにあります。実際にこれを実行しないでください。このPUTリクエストは技術的にべき等ですが、それがひどく壊れたアイデアではないという意味ではありません。

どのようにしてPATCHをべき等にすることができますか?

上記の例では、パッチがあった冪等。変更を加えましたが、同じ変更を何度も行った場合、常に同じ結果が返されます。メールアドレスを新しい値に変更したためです。

GET /users/1
{
    "username": "skwee357",
    "email": "skwee357@domain.com"
}
PATCH /users/1
{
    "email": "skwee357@gmail.com"       // new email address
}

GET /users/1
{
    "username": "skwee357",
    "email": "skwee357@gmail.com"       // email address was changed
}
PATCH /users/1
{
    "email": "skwee357@gmail.com"       // new email address... again
}

GET /users/1
{
    "username": "skwee357",
    "email": "skwee357@gmail.com"       // nothing changed since last GET
}

私の元の例、正確さのために修正

私はもともと非べき等性を示していると思った例がありましたが、それらは誤解を招く/不正確でした。ここでは例を使用しますが、それらを使用して別のことを説明します。同じエンティティに対する複数のPATCHドキュメントが、異なる属性を変更しても、PATCHを非べき等にしないでください。

ある時点でユーザーが追加されたとしましょう。これはあなたが出発している状態です。

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "skwee357@olddomain.com",
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "10001"
}

PATCHの後、エンティティが変更されます。

PATCH /users/1
{"email": "skwee357@newdomain.com"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "skwee357@newdomain.com",    // the email changed, yay!
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "10001"
}

その後、PATCHを繰り返し適用しても、同じ結果が引き続き得られます。メールは新しい値に変更されました。Aが入り、Aが出てくるので、これはべき等です。

1時間後、コーヒーを飲み、休憩を取った後、誰かが自分のパッチを持ってきます。郵便局はいくつかの変更を加えているようです。

PATCH /users/1
{"zip": "12345"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "skwee357@newdomain.com",  // still the new email you set
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "12345"                      // and this change as well
}

郵便局からのこのPATCHは、Eメールではなく、郵便番号のみを対象としているため、繰り返し適用すると、同じ結果が得られます。郵便番号は新しい値に設定されます。Aが入る、Aが出る、したがってこれべき等です。

翌日、パッチを再度送信することにしました。

PATCH /users/1
{"email": "skwee357@newdomain.com"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "skwee357@newdomain.com",
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "12345"
}

パッチは、昨日と同じ効果があります。メールアドレスを設定します。Aが入り、Aが出たので、これもべき等です。

私の元の答えで間違ったこと

私は重要な区別をしたいです(私の元の答えで私が間違ったこと)。多くのサーバーは、変更がある場合は変更を加えて、新しいエンティティの状態を送り返すことによってRESTリクエストに応答します。したがって、この応答が返ってきたときは、昨日返されたものとは異なります。郵便番号は前回受け取ったものではないためです。ただし、リクエストは郵便番号ではなく、電子メールにのみ関係していました。したがって、PATCHドキュメントは依然としてべき等です。PATCHで送信した電子メールは、エンティティの電子メールアドレスになります。

では、PATCHはいつべき等にならないのでしょうか?

この質問の完全な扱いについては、もう一度Jason Hoetgerの回答を参照してください。正直言って、彼がすでに持っている以上にこの部分に答えられるとは思えないからです。


2
この文は完全に正しくありません:「しかしそれはべき等です:Aが入るたびにBは常に出てきます」。たとえばGET /users/1、郵便局が郵便番号を更新する前に行ったGET /users/1後、郵便局の更新後に同じ要求を再度行った場合、2つの異なる応答(異なる郵便番号)が返されます。同じ "A"(GETリクエスト)が実行されますが、結果は異なります。しかし、GETは依然としてべき等です。
Jason Hoetger、2016

@JasonHoetger GETは安全です(変更が発生しないと想定されています)が、常にべき等であるとは限りません。違いがあります。RFC 2616秒を参照してください9.1
Dan Lowe 2016

1
@DanLowe:GETは、間違いなくべき等であることを保証されています。RFC 2616のセクション9.1.2と更新された仕様のRFC 7231セクション4.2.2では、「この仕様で定義されている要求メソッドのうち、PUT、DELETE、および安全な要求メソッドはべき等である」と正確に述べています。べき等は、「同じリクエストを行うたびに同じ応答が得られる」という意味ではありません。7231 4.2.2は次のように続けます。「元の要求が成功した場合でも、応答は異なる場合がありますが、要求を繰り返すと同じ意図した効果があります。
Jason Hoetger

1
... @JasonHoetger私はそれを認めるだろうが、私はそれがこの答えに関係しているか見ていない、PUTやPATCHを議論しているとは決してでもGETを言及
ダン・ロウ

1
ああ、@ JasonHoetgerからのコメントはそれを明確にしました:複数のべき等メソッド要求の結果ではなく、結果の状態だけが同一である必要があります。
トムラッセル

328

Dan Loweの優れた回答は、PUTとPATCHの違いに関するOPの質問に非常に完全に回答しましたが、PATCHがべき等ではない理由に関する質問への回答は、正確ではありません。

PATCHがべき等ではない理由を示すために、べき等の定義から始めることが役立ちます(Wikipediaから):

べき等という用語は、1回または複数回実行した場合に同じ結果を生成する操作を説明するために、より包括的に使用されます[...]べき等関数は、プロパティf(f(x))= f(x)を持つものです任意の値x。

よりアクセスしやすい言語では、べき等のPATCHは次のように定義できます。パッチドキュメントでリソースをPATCHした後、同じパッチドキュメントで同じリソースへの後続のすべてのPATCH呼び出しはリソースを変更しません。

逆に、べき等でない操作とは、f(f(x))!= f(x)の操作であり、PATCHの場合、次のように記述できます。同じパッチドキュメントリソースを変更します。

べき等でないPATCHを説明するために、/ usersリソースがあり、呼び出しGET /usersが現在ユーザーのリストを返すと仮定します。

[{ "id": 1, "username": "firstuser", "email": "firstuser@example.org" }]

OPの例のように、/ users / {id}をPATCHするのではなく、サーバーが/ userのPATCHを許可するとします。このPATCHリクエストを発行しましょう:

PATCH /users
[{ "op": "add", "username": "newuser", "email": "newuser@example.org" }]

パッチドキュメントは、ユーザーnewuserのリストに呼び出される新しいユーザーを追加するようサーバーに指示します。これを初めて呼び出した後、次のGET /usersように戻ります。

[{ "id": 1, "username": "firstuser", "email": "firstuser@example.org" },
 { "id": 2, "username": "newuser", "email": "newuser@example.org" }]

ここで、上記とまったく同じ PATCHリクエストを発行するとどうなりますか?(この例では、/ usersリソースでユーザー名の重複が許可されていると仮定します。)「op」は「add」なので、新しいユーザーがリストに追加され、その後に次のようにGET /users返されます。

[{ "id": 1, "username": "firstuser", "email": "firstuser@example.org" },
 { "id": 2, "username": "newuser", "email": "newuser@example.org" },
 { "id": 3, "username": "newuser", "email": "newuser@example.org" }]

/ usersリソースが再び変更されましたがまったく同じエンドポイントに対してまったく同じ PATCH を発行しました。PATCHがf(x)の場合、f(f(x))はf(x)と同じではないため、この特定のPATCHはべき等ではありません

PATCHがべき等であることは保証されていませんが、PATCH仕様には、特定のサーバーですべてのPATCH操作をべき等にすることを妨げるものはありません。RFC 5789は、べき等PATCHリクエストからの利点さえ予測しています。

PATCH要求は、べき等になるように発行できます。これは、同様の時間枠での同じリソース上の2つのPATCH要求間の衝突による悪い結果を防ぐのにも役立ちます。

ダンの例では、彼のPATCH操作は実際にはべき等です。その例では、/ users / 1エンティティがPATCHリクエスト間で変更されましたが、PATCHリクエストのためではありませんでし。郵便番号が変更されたのは、実際には郵便局の別のパッチドキュメントでした。郵便局の異なるPATCHは異なる操作です。PATCHがf(x)の場合、郵便局のPATCHはg(x)です。f(f(f(x))) = f(x)べき等性はそれを述べていますが、それについては保証しませんf(g(f(x)))


11
サーバーがでのPUTの発行も許可していると仮定すると/users、これによりPUTも非べき等になります。これは、サーバーが要求を処理するように設計されている方法にかかっています。
Uzair Sajid 2017

13
したがって、PATCH操作でのみAPIを構築できます。次に、http VERBSを使用してリソースにCRUDアクションを実行するというREST原則は何になりますか?ここでは、パッチの境界が紳士の境界を複雑にしすぎていませんか?
ボーア

6
PUTがコレクション(例:)に実装されている場合/users、PUTリクエストはそのコレクションのコンテンツを置き換える必要があります。したがって、PUT /usersはユーザーのコレクションを期待し、他のすべてを削除する必要があります。これはべき等です。/ usersエンドポイントでそのようなことをすることはおそらくありません。しかし、のようなもの/users/1/emailsはコレクションであり、コレクション全体を新しいもので置き換えることを許可することは完全に有効である可能性があります。
Vectorjohn

5
この答えはべき等の優れた例を提供しますが、これは典型的なRESTシナリオで水を濁らせる可能性があると思います。この場合、op特定のサーバー側ロジックをトリガーする追加のアクションを含むPATCHリクエストがあります。これにより、サーバーとクライアントは、opフィールドがサーバー側のワークフローをトリガーするために渡す特定の値を認識する必要があります。より単純なRESTシナリオでは、このタイプのop機能は悪い習慣であり、HTTP動詞を介して直接処理される可能性があります。
ivandov

7
コレクションに対してPATCHを発行することは考えず、POSTとDELETEのみを発行します。これは本当に行われたのですか?したがって、PATCHはすべての実用的な目的でべき等と見なすことができますか?
トムラッセル

72

私もこれに興味があり、興味深い記事をいくつか見つけました。私はあなたの質問に完全には答えられないかもしれませんが、これは少なくともいくつかのより多くの情報を提供します。

http://restful-api-design.readthedocs.org/en/latest/methods.html

HTTP RFCは、PUTが完全な新しいリソース表現を要求エンティティとして受け取る必要があることを指定しています。これは、たとえば特定の属性のみが指定されている場合、それらを削除する(つまりnullに設定する)必要があることを意味します。

その場合、PUTはオブジェクト全体を送信する必要があります。例えば、

/users/1
PUT {id: 1, username: 'skwee357', email: 'newemail@domain.com'}

これにより、効果的に電子メールが更新されます。PUTがあまり効果的でない理由は、実際に1つのフィールドを変更し、ユーザー名を含めるだけでは役に立たないためです。次の例は違いを示しています。

/users/1
PUT {id: 1, email: 'newemail@domain.com'}

ここで、PUTが仕様に従って設計されている場合、PUTはユーザー名をnullに設定し、次の結果を返します。

{id: 1, username: null, email: 'newemail@domain.com'}

PATCHを使用する場合、指定したフィールドのみを更新し、例のように残りはそのままにします。

次のパッチのテイクは、これまで見たことのないものとは少し異なります。

http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/

PUT要求とPATCH要求の違いは、サーバーが囲まれたエンティティを処理してRequest-URIで識別されるリソースを変更する方法に反映されます。PUT要求では、囲まれたエンティティーは、起点サーバーに保管されているリソースの変更バージョンであると見なされ、クライアントは保管されているバージョンの置き換えを要求しています。ただし、PATCHでは、囲まれたエンティティに、現在オリジンサーバーにあるリソースを変更して新しいバージョンを作成する方法を説明する一連の指示が含まれています。PATCHメソッドは、Request-URIによって識別されるリソースに影響します。また、他のリソースに副作用がある場合もあります。つまり、PATCHを適用することで、新しいリソースを作成したり、既存のリソースを変更したりできます。

PATCH /users/123

[
    { "op": "replace", "path": "/email", "value": "new.email@example.org" }
]

あなたは多かれ少なかれ、パッチをフィールドを更新する方法として扱っています。したがって、部分オブジェクトを送信する代わりに、オペレーションを送信します。つまり、Eメールを値に置き換えます。

記事はこれで終わります。

Fieldingの論文ではリソースを部分的に変更する方法が定義されていないため、PATCHは実際には真にREST API向けに設計されていないことに言及する価値があります。しかし、ロイフィールディング氏自身は、部分的なPUTはRESTfulではないため、PATCHは最初のHTTP / 1.1プロポーザルのために作成されたものであると述べました。もちろん、完全な表現を転送するわけではありませんが、RESTでは表現を完全にする必要はありません。

多くのコメンテーターが指摘しているように、今、私がこの記事に特に同意するかどうかはわかりません。部分的な表現を介して送信することは、簡単に変更の説明になります。

私にとっては、PATCHの使用はまちまちです。ほとんどの場合、PUTをPATCHとして扱います。これまで気付いた唯一の本当の違いは、PUTが欠損値をnullに設定する必要があることです。それを行うための「最も正しい」方法ではないかもしれませんが、頑張ってコーディングしてください。


7
追加する価値があるかもしれません。WilliamDurandの記事(およびrfc 6902)には、「op」が「add」である例があります。これは明らかにべき等ではありません。
Johannes Brodwall 2016

2
または、より簡単にして、代わりにRFC 7396マージパッチを使用して、パッチJSONを構築しないようにすることができます。
Piotr Kula 2017年

NoSQLのは、何の列がないので、NoSQLのテーブルのために、パッチとプットの違いは、重要である
stackdave

18

PUTとPATCHの違いは次のとおりです。

  1. PUTはべき等である必要があります。これを実現するには、完全なリソース全体をリクエストの本文に含める必要があります。
  2. PATCHは非べき等にすることができます。つまり、あなたが説明した場合など、場合によってはそれがべき等になることもあります。

PATCHは、リソースを変更する方法をサーバーに伝えるために、いくつかの「パッチ言語」を必要とします。呼び出し元とサーバーは、「追加」、「置換」、「削除」などの「操作」を定義する必要があります。例えば:

GET /contacts/1
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "skwee357@olddomain.com",
  "state": "NY",
  "zip": "10001"
}

PATCH /contacts/1
{
 [{"operation": "add", "field": "address", "value": "123 main street"},
  {"operation": "replace", "field": "email", "value": "abc@myemail.com"},
  {"operation": "delete", "field": "zip"}]
}

GET /contacts/1
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "abc@myemail.com",
  "state": "NY",
  "address": "123 main street",
}

明示的な「操作」フィールドを使用する代わりに、パッチ言語は次のような規則を定義することで暗黙的にすることができます。

PATCHリクエストの本文:

  1. フィールドの存在は、そのフィールドを「置換」または「追加」することを意味します。
  2. フィールドの値がnullの場合は、そのフィールドを削除することを意味します。

上記の規則に従って、例のPATCHは次の形式を取ることができます。

PATCH /contacts/1
{
  "address": "123 main street",
  "email": "abc@myemail.com",
  "zip":
}

より簡潔でユーザーフレンドリーに見えます。ただし、ユーザーは基本的な規則に注意する必要があります。

上記の操作では、PATCHは依然としてべき等です。ただし、「インクリメント」や「追加」などの操作を定義すると、それがべき等ではなくなることが簡単にわかります。


7

TLDR-ダウンバージョン

PUT =>既存のリソースのすべての新しい属性を設定します。

PATCH =>既存のリソースを部分的に更新します(すべての属性が必要なわけではありません)。


3

以前のコメントですでに引用されているRFC 7231セクション4.2.2をより詳しく引用してコメントします。

リクエストメソッドは、そのメソッドによる複数の同一リクエストのサーバーへの意図した影響が、そのような単一のリクエストの影響と同じである場合、「べき等」と見なされます。この仕様で定義されている要求メソッドのうち、PUT、DELETE、および安全な要求メソッドはべき等です。

(...)

べき等メソッドは区別されます。これは、クライアントがサーバーの応答を読み取る前に通信障害が発生した場合に、要求を自動的に繰り返すことができるためです。たとえば、クライアントがPUTリクエストを送信し、応答が受信される前に基礎となる接続が閉じられた場合、クライアントは新しい接続を確立してべき等のリクエストを再試行できます。応答は異なる場合がありますが、元の要求が成功した場合でも、要求を繰り返すと同じ効果が得られることがわかっています。

では、べき等メソッドが繰り返しリクエストされた後、何が「同じ」であるべきでしょうか?サーバーの状態やサーバーの応答ではなく、意図した効果。特に、メソッドは「クライアントの観点から」べき等であるべきです。今、私はこの観点から、ダンロウの回答の最後の例(ここでは盗用したくない)が実際にPATCHリクエストが非べき等である可能性があることを示していると思います(の例よりも自然な方法で)Jason Hoetgerの回答)。

実際、最初のクライアントを意図したものを明示的に作成することで、例をもう少し正確にしてみましょう。このクライアントが、プロジェクトのユーザーのリストを調べて、メール郵便番号を確認するとします。彼はユーザー1から始め、zipは正しいが電子メールが間違っていることに気づきました。彼は完全に正当なPATCHリクエストでこれを修正することを決定し、

PATCH /users/1
{"email": "skwee357@newdomain.com"}

これが唯一の修正なので。現在、ネットワークの問題のためにリクエストは失敗し、数時間後に自動的に再送信されます。その間、別のクライアントがユーザー1のzipを(誤って)変更しました。次に、同じPATCHリクエストを2回送信しても、間違ったzipになるため、クライアントの意図した効果られません。したがって、このメソッドはRFCの意味ではべき等ではありません。

代わりに、クライアントがPUT要求を使用して電子メールを修正し、ユーザー1のすべてのプロパティを電子メールと共にサーバーに送信する場合、後で要求を再送信する必要があり、ユーザー1が変更されている場合でも、意図した効果が得られますその間--- 2番目のPUT要求は最初の要求以降のすべての変更を上書きするため。


2

私の控えめな意見では、べき等性は次のことを意味します:

  • プット:

競合するリソース定義を送信するので、結果のリソース状態はPUTパラメータで定義されているとおりです。同じPUTパラメータでリソースを更新するたびに、結果の状態はまったく同じです。

  • パッチ:

リソース定義の一部のみを送信したため、その間に他のユーザーがこのリソースのOTHERパラメータを更新している可能性があります。したがって、同じパラメーターとその値を持つパッチが連続すると、リソースの状態が異なる可能性があります。例えば:

次のように定義されたオブジェクトを想定します。

車:-色:黒、-タイプ:セダン、-座席:5

私はそれをパッチします:

{赤色'}

結果のオブジェクトは次のとおりです。

車:-色:赤、-タイプ:セダン、-座席:5

次に、他の一部のユーザーがこの車に次のパッチを適用します。

{タイプ: 'ハッチバック'}

したがって、結果のオブジェクトは次のとおりです。

車:-色:赤、-タイプ:ハッチバック、-座席:5

ここで、このオブジェクトに再度パッチを適用すると:

{赤色'}

結果のオブジェクトは次のとおりです。

車:-色:赤、-タイプ:ハッチバック、-座席:5

私が以前に得たものと何が違うのですか!

これが、PUTがべき等であるのにPATCHがべき等ではない理由です。


1

べき等性に関する議論を締めくくるために、RESTコンテキストでべき等性を2つの方法で定義できることに注意する必要があります。最初にいくつかのことを形式化しましょう:

リソースは、その終域が文字列のクラスであることとの関数です。つまり、リソースはのサブセットでString × Anyあり、すべてのキーが一意です。リソースのクラスを呼び出しましょうRes

リソースに対するREST操作は関数f(x: Res, y: Res): Resです。REST操作の2つの例は次のとおりです。

  • PUT(x: Res, y: Res): Res = x、および
  • PATCH(x: Res, y: Res): Res、のように機能しPATCH({a: 2}, {a: 1, b: 3}) == {a: 2, b: 3}ます。

(この定義は、具体的には約主張するように設計されたPUTPOSTし、例えば、上あまり意味がないGETPOST、それは持続性を気にしないように、)。

今、固定することによりx: Res(informaticallyカリー化を使用して、話す)、PUT(x: Res)およびPATCH(x: Res)タイプの単変量関数ですRes → Res

  1. 関数がg: Res → Res呼び出された世界的に冪等する場合、g ○ g == gすなわち任意のためy: Resg(g(y)) = g(y)

  2. x: Resリソースとしましょうk = x.keys。関数g = f(x)左べき等と呼ばれ、それぞれについてy: Res、私たちは持っていg(g(y))|ₖ == g(y)|ₖます。適用されたキーを見ると、結果は基本的に同じになるはずです。

したがって、PATCH(x)はグローバルにべき等ではなく、べき等のままです。そして、ここで重要なのは左べき等性です。リソースのいくつかのキーにパッチを適用する場合、再度パッチを適用する場合にそれらのキーを同じにして、残りのリソースは気にしません。

そしてRFCがPATCHがべき等ではないことについて話しているとき、それはグローバルなべき等性について話している。まあ、それがグローバルにべき等ではないのは良いことです、そうでなければそれは壊れた操作であったでしょう。


さて、Jason Hoetgerの答えは、PATCHがべき等であることさえないことを実証しようとしていますが、それはあまりにも多くのことを壊しており、

  • まず、PATCHはセットで使用されますが、PATCHはマップ/辞書/ Key-Valueオブジェクトで機能するように定義されています。
  • 誰かが本当にPATCHをセットに適用したい場合、使用すべき自然な翻訳があります:t: Set<T> → Map<T, Boolean>、で定義されx in A iff t(A)(x) == Trueます。この定義を使用すると、パッチ適用はべき等です。
  • この例では、この変換は使用されず、PATCHはPOSTのように機能します。まず、オブジェクトのIDが生成されるのはなぜですか?そして、それはいつ生成されますか?オブジェクトが最初にセットの要素と比較され、一致するオブジェクトが見つからない場合、IDが生成され、プログラムは異なる動作をします(と{id: 1, email: "me@site.com"}一致する必要があり{email: "me@site.com"}ます。そうでない場合、プログラムは常に壊れており、PATCHはおそらくパッチ)。セットと照合する前にIDが生成されると、再びプログラムが壊れます。

この例で壊れているものの半分を壊すことで、PUTが非べき等である例を作ることができます:

  • 生成された追加機能の例は、バージョン管理です。1つのオブジェクトに対する変更の数を記録することができます。この場合、PUTはべき等ではありません。PUT /user/12 {email: "me@site.com"}結果{email: "...", version: 1}は1回{email: "...", version: 2}目と2回目です。
  • IDをいじくり回すと、オブジェクトが更新されるたびに新しいIDが生成され、結果がべき等でないPUTになる可能性があります。

上記の例はすべて、実際に遭遇する可能性のある例です。


私の最後のポイントは、PATCHはグローバルにべきであってはならないということです。そうしないと、望ましい効果が得られません。残りの情報に触れずにユーザーのメールアドレスを変更し、同じリソースにアクセスしている別のパーティーの変更を上書きしたくない場合。


-1

追加する1つの追加情報は、エンティティ全体ではなくデータの一部のみが送信されるため、PATCH要求はPUT要求と比較して使用する帯域幅が少ないということです。したがって、PATCHリクエストを使用して(1〜3レコード)のような特定のレコードを更新し、PUTリクエストを使用して大量のデータを更新します。つまり、あまり考えすぎたり、心配しすぎたりしないでください。

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