HTTP/1.1の「PUT」を正しく使いこなす:冪等性とリソース置換の深い話
Web APIを設計する際、`GET`で取得し、`POST`で作成し、`DELETE`で消す。ここまでは誰でも通る道だ。しかし、実務の現場で「リソースの更新」を設計する段になると、`PUT`と`PATCH`、あるいは`POST`の使い分けで手が止まるエンジニアが多い。
今日は、HTTP/1.1の仕様において最も誤解されやすく、かつ強力なメソッドである`PUT`の本質について、現場のインフラ視点から深掘りしていこう。なぜ`PUT`は「冪等(べきとう)」でなければならないのか。そして、なぜそれが「完全置換」なのか。その意味を理解すれば、APIの堅牢性は一段階引き上がる。
—
1. PUTの定義:それは「上書き」ではなく「置換」だ
RFC 9110(旧RFC 7231)において、`PUT`メソッドは「ターゲットリソースの現在の状態を、リクエストのペイロードで提供された状態に置換する」と定義されている。
ここで重要なのは、「リソースそのものを差し替える」という概念だ。`PATCH`のように一部のフィールドだけを更新する差分更新ではない。`PUT`は、指定したURIに送られてきたデータが「そのリソースの全て」であると見なす。
なぜ「冪等(Idempotency)」が重要なのか
冪等とは、「同じ操作を何回繰り返しても、システムの状態が変わらない」性質を指す。
例えば、ネットワーク障害でパケットがロスし、クライアントが再送を試みる場面を想像してほしい。
- POSTの場合: 2回送ると、2回分リソースが作成される可能性がある(非冪等)。
- PUTの場合: 1回目も2回目も「URIの結果がこのデータであること」を保証するため、何回実行しても最終的なリソースの状態は同じになる(冪等)。
この「何度叩いても安全」という性質こそが、信頼性の高い分散システムを構築する際の生命線となる。
—
2. 通信フローから見るPUTの挙動
`PUT`リクエストのシーケンスは非常にシンプルだ。しかし、インフラエンジニアとしては「中身」に注目する必要がある。
[Client] [Server]
| |
|– PUT /users/123 —->| <- URIで対象リソースを特定
| { "name": "Alice" } | <- ボディでリソースを完全定義
| |
|<-- 200 OK / 204 No C--| <- 成功なら状態を報告
| |
もし、サーバー側が指定されたURIにリソースを持っていない場合、`PUT`は新規作成(Create)として振る舞うことも仕様上許容されている。しかし、その場合でも「URIはクライアントが決定する」という点が`POST`との決定的な違いだ。
---
3. 実践:PUTを叩くためのコード実装
理論だけでは現場は回らない。実際に`curl`や`Python`を用いて、この挙動を検証してみよう。
curl での検証
既存のリソースを完全に置き換える
curl -X PUT https://api.example.com/items/5 \
-H “Content-Type: application/json” \
-d ‘{“name”: “Updated Item”, “price”: 1500}’ # このデータが全てとなる
Python (requests) での検証
import requests
url = “https://api.example.com/items/5”
payload = {“name”: “Updated Item”, “price”: 1500}
冪等性を意識したPUTリクエスト
ネットワークエラーが発生しても、再送すれば結果は同じになる
response = requests.put(url, json=payload)
if response.status_code in [200, 204]:
print(“リソースの置換に成功しました”)
else:
print(f”エラー発生: {response.status_code}”)
—
4. 現場でハマる「落とし穴」とデバッグの極意
私がこれまで見てきた障害の中で、`PUT`にまつわるトラブルは以下の2点に集約されることが多い。
① 「部分更新」をPUTで行ってしまう設計ミス
「名前だけ変えたいのに、`PUT`を送ったら他のフィールドが消えた」という問い合わせは後を絶たない。これは`PUT`が完全置換であることを忘れた設計によるものだ。部分更新が必要なら、素直に`PATCH`を使うか、API側でリソースのバリデーションを厳格に行う必要がある。
② 冪等性が崩れる実装(サーバー側の副作用)
サーバー側で`PUT`の処理内に「カウンターをインクリメントする」といった副作用を仕込むと、冪等性は即座に破綻する。
「PUTのハンドラーにはカウンターもログの重複記録も入れない」。これが鉄則だ。
トラブルシューティングTips
もし、`PUT`を叩いたはずなのに予期せぬ挙動をする場合は、まず以下を確認せよ。
1. HTTPステータスコード: `200 OK` なのか `201 Created` なのか。`201`が返っているなら、クライアントは「新規作成」を意図していないのにサーバーがそう解釈している可能性がある。
2. キャッシュ: `PUT`に対するキャッシュが中間プロキシ(CDN等)で効いていないか確認せよ。通常`PUT`はキャッシュされないが、不適切な設定は時に悪さをする。
3. リクエストボディの妥当性: サーバー側で「足りないフィールド」をどう扱っているか(NULLで埋めるのか、デフォルト値を入れるのか)。ここが不明確だと、クライアント側でリソースを破壊する原因になる。
—
最後に:プロトコルを信じるということ
`PUT`を正しく使うということは、ネットワークの不安定さとどう向き合うかを定義することに他ならない。冪等性を理解し、リソースのライフサイクルを制御できるようになれば、あなたのAPIはネットワークの揺らぎに対しても強靭なものになるはずだ。
次は、`PATCH`メソッドを使った差分更新の安全な実装について議論しよう。あれはあれで、また面白い沼が待っている。
現場からは以上だ。何かあればまたいつでも聞いてくれ。
コメント