注:最初にRESTについて読むことに時間を費やしたとき、べき等性は正しく理解しようとする混乱する概念でした。追加のコメント(およびJason Hoetgerの回答)が示すように、私はまだ元の回答では完全に正しく理解していませんでした。しばらくの間、Jasonの効果的な盗用を避けるために、この回答を大幅に更新することに抵抗してきましたが、(コメントで)求められたため、現在編集しています。
私の回答を読んだ後、この質問に対するJason Hoetgerの優れた回答も読むことをお勧めします。Jasonから単に盗むことなく、私の回答をより良くするように努めます。
なぜPUTはべき等なのですか?
RFC 2616の引用で述べたように、PUTはべき等と見なされます。リソースをPUTする場合、次の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の回答を参照してください。正直言って、彼がすでに持っている以上にこの部分に答えられるとは思えないからです。