First of all, some definitions:
PUT is defined in Section 9.6 RFC 2616:
The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.
PATCH is defined in RFC 5789:
The PATCH method requests that a set of changes described in the request entity be applied to the resource identified by the Request- URI.
Also according to RFC 2616 Section 9.1.2 PUT is Idempotent while PATCH is not.
Now let us take a look at a real example. When I do POST to /users
with the data {username: 'skwee357', email: '[email protected]'}
and the server is capable of creating a resource, it will respond with 201 and resource location (lets assume /users/1
) and any next call to GET /users/1
will return {id: 1, username: 'skwee357', email: '[email protected]'}
.
Now let us say I want to modify my email. Email modification is considered "a set of changes" and therefore I should PATCH /users/1
with "patch document". In my case it would be the JSON document: {email: '[email protected]'}
. The server then returns 200 (assuming permission are ok). This brings me to first question:
- PATCH is NOT idempotent. It said so in RFC 2616 and RFC 5789. However if I issue the same PATCH request (with my new email), I will get the same resource state (with my email being modified to the requested value). Why is PATCH not then idempotent?
PATCH is a relatively new verb (RFC introduced in March 2010), and it comes to solve the problem of "patching" or modifying a set of fields. Before PATCH was introduced, everybody used PUT to update resources. But after PATCH was introduced, it leaves me confused about what PUT is used for. And this brings me to my second (and the main) question:
- What is the real difference between PUT and PATCH? I have read somewhere that PUT might be used to replace entire entity under specific resource, so one should send the full entity (instead of set of attributes as with PATCH). What is the real practical usage for such case? When would you like to replace / overwrite an entity at a specific resource URI and why is such an operation not considered updating / patching the entity? The only practical use case I see for PUT is issuing a PUT on a collection, i.e.
/users
to replace the entire collection. Issuing PUT on a specific entity makes no sense after PATCH was introduced. Am I wrong?
ベストアンサー1
注: RESTについて初めて読んだとき、べき等性は理解するのが難しい概念でした。最初の回答では、まだ完全に理解できていなかったのですが、その後のコメント(およびジェイソン・ホートガーの回答) が示しています。しばらくの間、Jason を盗用することを避けるために、この回答を大幅に更新することを控えていましたが、コメントで編集するように求められたため、今は編集しています。
私の回答を読んだ後、次の記事も読むことをお勧めしますJason Hoetgerの素晴らしい回答この質問に対して、私はジェイソンの回答をそのまま盗用するのではなく、より良い回答をするよう努めます。
PUT がべき等なのはなぜですか?
RFC 2616 の引用で述べたように、PUT はべき等性があると考えられています。リソースを PUT する場合、次の 2 つの仮定が適用されます。
コレクションではなく、エンティティを参照しています。
提供しているエンティティは完全です (エンティティ全体)。
例の1つを見てみましょう。
{ "username": "skwee357", "email": "[email protected]" }
あなたが提案したように、この文書を にPOSTすると/users
、次のようなエンティティが返される可能性があります。
## /users/1
{
"username": "skwee357",
"email": "[email protected]"
}
後でこのエンティティを変更する場合は、PUT と PATCH のどちらかを選択します。PUT は次のようになります。
PUT /users/1
{
"username": "skwee357",
"email": "[email protected]" // new email address
}
PATCH を使用して同じことを実現できます。次のようになります。
PATCH /users/1
{
"email": "[email protected]" // new email address
}
これら 2 つの違いはすぐにわかります。PUT にはこのユーザーのすべてのパラメータが含まれていましたが、PATCH には変更されているパラメータ ( email
) のみが含まれていました。
PUT を使用する場合、完全なエンティティを送信し、その完全なエンティティがその URI の既存のエンティティを置き換えるものと想定されます。上記の例では、PUT と PATCH は同じ目的を達成します。どちらもこのユーザーの電子メール アドレスを変更します。ただし、PUT はエンティティ全体を置き換えることで処理しますが、PATCH は指定されたフィールドのみを更新し、他のフィールドはそのままにします。
PUT リクエストにはエンティティ全体が含まれるため、同じリクエストを繰り返し発行すると、常に同じ結果になります (送信したデータは、エンティティのデータ全体になります)。したがって、PUT はべき等です。
PUTの使い方が間違っている
上記の PATCH データを PUT リクエストで使用するとどうなりますか?
GET /users/1
{
"username": "skwee357",
"email": "[email protected]"
}
PUT /users/1
{
"email": "[email protected]" // new email address
}
GET /users/1
{
"email": "[email protected]" // new email address... and nothing else!
}
(この質問の目的上、サーバーに特定の必須フィールドがなく、これが許可されると想定しています...実際にはそうではない可能性があります。)
PUT を使用しましたが、 のみを指定したためemail
、このエンティティには のみが含まれます。これにより、データが失われました。
この例は説明目的でここにあります。実際には決してこれを実行しないでください (もちろん、省略されたフィールドを削除することが目的である場合は別ですが、その場合は PUT を正しく使用しています)。この PUT 要求は技術的にはべき等ですが、それがひどく壊れたアイデアではないということではありません。
PATCH はどのようにしてべき等性を持つのでしょうか?
上記の例では、PATCHはべき等でした。変更を加えましたが、同じ変更を何度も繰り返しても、常に同じ結果が返されます。つまり、電子メール アドレスが新しい値に変更されたことになります。
GET /users/1
{
"username": "skwee357",
"email": "[email protected]"
}
PATCH /users/1
{
"email": "[email protected]" // new email address
}
GET /users/1
{
"username": "skwee357",
"email": "[email protected]" // email address was changed
}
PATCH /users/1
{
"email": "[email protected]" // new email address... again
}
GET /users/1
{
"username": "skwee357",
"email": "[email protected]" // nothing changed since last GET
}
正確さのために修正した私のオリジナルの例
元々、非べき等性を示していると思われる例がありましたが、誤解を招く/不正確なものでした。この例はそのままにして、別のことを説明するのに使用します。つまり、同じエンティティに対して複数の PATCH ドキュメントを作成し、異なる属性を変更しても、PATCH が非べき等性になるわけではないということです。
過去のある時点でユーザーが追加されたとします。これが開始時の状態です。
{
"id": 1,
"name": "Sam Kwee",
"email": "[email protected]",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
PATCH 後、エンティティが変更されます。
PATCH /users/1
{"email": "[email protected]"}
{
"id": 1,
"name": "Sam Kwee",
"email": "[email protected]", // the email changed, yay!
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
その後、PATCH を繰り返し適用すると、同じ結果が引き続き得られます。つまり、電子メールが新しい値に変更されます。A が入力されると、A が出力されるため、これはべき等性です。
1時間後、コーヒーを淹れて休憩していると、別の人が自分のPATCHを持ってやって来ました。どうやら郵便局が何か変更を加えているようです。
PATCH /users/1
{"zip": "12345"}
{
"id": 1,
"name": "Sam Kwee",
"email": "[email protected]", // still the new email you set
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "12345" // and this change as well
}
郵便局からのこの PATCH は電子メールには関係なく、郵便番号のみに関係するため、繰り返し適用しても同じ結果が得られます。郵便番号は新しい値に設定されます。A が入ると A が出てくるので、これもべき等です。
翌日、PATCH を再度送信することにしました。
PATCH /users/1
{"email": "[email protected]"}
{
"id": 1,
"name": "Sam Kwee",
"email": "[email protected]",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "12345"
}
あなたのパッチは昨日と同じ効果があります。電子メール アドレスを設定しました。A が入り、A が出たので、これもべき等です。
最初の回答で間違っていた点
重要な違いを述べておきたいと思います (最初の回答では間違っていました)。多くのサーバーは、REST リクエストに応答して、新しいエンティティ状態 (変更がある場合) を返します。そのため、この応答が返されると、郵便番号が前回受信したものではないため、昨日返された応答とは異なります。ただし、リクエストは郵便番号ではなく、電子メールのみに関係しています。そのため、PATCH ドキュメントは依然としてべき等性があり、PATCH で送信した電子メールがエンティティの電子メール アドレスになります。
では、PATCH がべき等性を持たないのはどのような場合でしょうか?
この問題の詳しい説明については、再度、ジェイソン・ホートガーの回答これですでに十分な答えが得られました。