PUTリクエスト徹底解説:意味・冪等性・POST/PATCHとの違いと実務のベストプラクティス

PUTリクエストとは — HTTPにおける意味と実務での扱い方

Web/HTTP APIを設計・実装する際、「更新」をどう表現するかは重要な判断です。PUT(HTTP PUT)は、リソースの「完全な置換(または作成)」を意図したメソッドで、REST設計でしばしば更新操作に割り当てられます。本稿では、RFCに基づくPUTの正式な意味、POSTやPATCHとの違い、ステータスコードや条件付きリクエスト、キャッシュやセキュリティ上の注意点、実運用でのベストプラクティスまで詳しく解説します。

1. PUTの基本的な意味(RFCに準拠した説明)

PUTは対象URIに「含まれる表現(representation)」をアップロードし、そのURIの資源を指定された表現で置き換えることを意図したメソッドです。RFC 7231では、PUTは「冪等(idempotent)」であると定義されています。つまり、同じPUTリクエストを何度送信しても副作用は一回分と等しくなり、繰り返し呼び出しても結果は同じであるべきです。

  • リソースが既に存在する場合:サーバはそのリソースをクライアントから送られた表現で置き換える(完全置換が期待される)。
  • リソースが存在しない場合:PUTで新たにリソースを作成してもよい(サーバの実装に依存)。
  • PUTは「安全(safe)」ではない:変更を伴うためGETのように取り消し不要な読み取り専用ではない。

2. POSTとの違い

  • URIの決定権:PUTはクライアントが目的のURIを指定して送る(例:/users/123 に対するPUT)。一方、POSTは通常コレクションに送られ、サーバが新しいリソースの識別子(URI)を生成する用途に適する(例:/users に POST → サーバが /users/456 を作る)。
  • 意味:POSTは「処理を要求する」や「部分的作成・アクション」に広く使える一方、PUTは「指定された表現でそのURIの資源を置き換える」ことが目的。
  • 冪等性:POSTは一般には非冪等(同じ呼び出しを繰り返すと複数作成されるなど)、PUTは冪等。

3. PATCHとの違い

PATCH(RFC 5789)は部分的更新のために設計されたメソッドです。PUTが全体の表現での置換を意図するのに対して、PATCHは差分や部分更新指示(JSON Merge Patch / JSON Patch など)を送る用途に使います。大量データのやり取りや、クライアントが現在の完全な表現を持っていない場合はPATCHの方が自然なことが多いです。

4. HTTPヘッダと条件付きリクエスト(楽観的ロック)

複数クライアントが同じリソースを更新する可能性がある場合、楽観的ロック(楽観的同時更新制御)が必要です。HTTPはこれをサポートするために条件付きリクエストヘッダを提供します。

  • If-Match: クライアントが知っているETagと一致する場合のみ適用する。ETagが一致しなければ412 Precondition Failedを返す。これにより「自分の知っているバージョンとの一致を保証してから更新する」ことができる。
  • If-None-Match: 通常は「存在しないこと」を保証して新規作成目的に使う(If-None-Match: *)。
  • ETag:サーバはリソース表現のETagをレスポンスで提供できる。クライアントはそれをIf-Matchにセットして更新の整合性を保てる。

5. ステータスコード(PUTに対する代表的な応答)

  • 200 OK:リクエスト成功かつサーバがリソース表現(更新後のもの)をレスポンスに含める場合。
  • 201 Created:PUTで新規リソースが作成された場合。LocationヘッダでURIを示すことが望ましい。
  • 204 No Content:成功したがボディを返さない場合(よく使われる)。
  • 412 Precondition Failed:If-Matchなどの事前条件が満たされなかった場合。
  • 409 Conflict:クライアントとサーバ間で資源状態の競合などがある場合に使用されることがある。

6. キャッシュとプロキシの挙動

PUT自体は通常「安全」ではないため、キャッシュが自動的に用いる対象ではありません。ただし、PUTに対するレスポンスがCache-ControlヘッダやETagなどのキャッシュ指示を含む場合、レスポンスはキャッシュ可能になり得ます。プロキシや中継ノードは非冪等なメソッドを自動再試行しない方が安全ですが、冪等であるPUTは、ネットワーク障害時にクライアント側や中間ノードで再送が比較的許容されやすいという利点があります。

7. 実装上の注意点とよくある誤解

  • 「PUTは必ず完全置換」だが、サーバ実装が部分更新を許すこともある:これは仕様上の期待と実装が必ずしも一致しない例なので、APIは明確にドキュメント化すること。
  • クライアント生成のIDとサーバ生成のIDの切り分け:クライアントがURIを指定できる場合はPUTが向いている。サーバがIDを生成する(自動採番など)場合はPOSTの方が自然。
  • 安全性(CSRF等):PUTも副作用を伴うのでCSRF対策や認証・認可が必要。ブラウザでは通常PUTリクエストはAJAXなどで送られ、CSRFトークンの扱いに注意。
  • ネットワーク再試行:PUTは冪等であるため、通信障害時にリトライ戦略を取りやすいが、リトライの前にリクエストボディが同一であることを確認する必要がある。

8. 実用的な例(curlでのPUT)

基本的なPUTリクエスト例(JSONボディを送る):

curl -X PUT "https://api.example.com/users/123" \
  -H "Content-Type: application/json" \
  -d '{"id":123,"name":"山田太郎","email":"taro@example.com"}'

条件付き更新(ETagを使った楽観的ロック)の例:

curl -X PUT "https://api.example.com/users/123" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "etag-value-here"' \
  -d '{"id":123,"name":"山田太郎","email":"taro.new@example.com"}'

9. ベストプラクティスまとめ

  • APIのドキュメントでPUTの振る舞い(完全置換か部分更新か、生成されるステータスコードなど)を明確にする。
  • クライアント生成URIの更新や置換にはPUT、サーバ生成IDの作成にはPOSTを使うという原則を守る。
  • 同時更新対策としてETag + If-Match等の条件付きリクエストを利用する。必要に応じてPrecondition Required(428)も検討する。
  • 部分更新が頻繁に発生するならPATCHを検討する(PUTで無理に部分更新を実装するとAPI利用者が混乱する)。
  • セキュリティ:認証・認可、CSRF対策(ブラウザからの呼び出し時)、通信のTLS化は必須。
  • ログや監査:置換操作は重要なので、変更履歴や監査ログを残すことを推奨。

10. まとめ

PUTはHTTPにおける「指定URIの資源を指定の表現で置き換える」ことを意図したメソッドで、冪等性が特徴です。RESTful API設計においては、クライアントがURIを決めて資源を更新・作成する用途に適していますが、部分更新やサーバ生成のIDとの相性、同時更新時の整合性確保など実務上の注意点があります。API実装者は仕様(RFC)に基づく正しい理解を持ちつつ、自身のAPIの振る舞いを明確にドキュメント化することが重要です。

参考文献