ASP.NET Coreでプロフィール取得をPUTにしても、実装次第では200 OKで普通に取得できてしまいます。しかしHTTPメソッドには「周辺機器や運用が期待する意味」があり、そこから外れるとキャッシュ、CORS、監視、SDK生成などで“後から”不具合と面倒が増えます。PUTで取れてしまう理由と、GETを使うべき技術的な根拠、現場での落とし穴、直し方までまとめます。
PUTで「取得できてしまう」理由は、サーバー実装がそうなっているだけ
まず前提として、HTTPは「このメソッドでこの処理をしなければならない」と強制する仕組みではありません。サーバー側がPUTリクエストに対して“DBから読んで返すだけ”の処理を書けば、PUTでも取得できますし、ステータスコードも200 OKで返せます。
つまり、今の状態は次のように整理できます。
- HTTPがPUTを「取得禁止」にしているわけではない
- “取得できる”のは、サーバーがPUTを取得として実装しているから
- 問題は「動くかどうか」ではなく、「標準の意味(セマンティクス)を外したときに周辺が壊れる/最適化が効かない」こと
HTTPメソッドの意味は、サーバーとクライアントの間だけの約束ではなく、途中に挟まるあらゆる仕組み(ブラウザ、プロキシ、CDN、API Gateway、WAF、監視、SDK、ドキュメント生成など)が前提として使っています。ここを崩すと、バグや運用コストが“積み立て式”で増えます。
GETとPUTの「意味」を誤解しないための整理
HTTPメソッドは「URL + メソッド + ヘッダー + ボディ」で構成されるリクエストのうち、特に“操作の意味”を表す重要な要素です。REST APIを意識するなら、メソッドの意味を守ることは、単なる作法ではなく相互運用性の土台になります。
| メソッド | 標準的な意味 | 安全(Safe) | 冪等(Idempotent) | キャッシュされやすさ | 代表的な用途 |
|---|---|---|---|---|---|
| GET | リソースの取得(読み取り) | はい(サーバー状態を変えない前提) | はい | 高い(中間装置が最適化しやすい) | プロフィール表示、一覧取得、詳細取得 |
| POST | 処理の実行/作成(サーバー側に何かを起こす) | いいえ | いいえ(一般に) | 低い | 新規作成、ログイン、複雑検索(ボディが必要なクエリ) |
| PUT | リソースの置き換え更新(存在しなければ作成する場合も) | いいえ | はい | 低い | プロフィール全体の更新、設定の置換 |
| PATCH | リソースの部分更新(差分更新) | いいえ | 状況による(設計次第) | 低い | 一部項目だけ変更(表示名だけ更新など) |
| DELETE | リソースの削除 | いいえ | はい(一般に) | 低い | アカウント削除、下書き削除 |
ここで重要なのは、「冪等=安全」ではないことです。
- 安全(Safe):リクエストがサーバー状態を変えない前提(読み取り専用の期待)
- 冪等(Idempotent):同じリクエストを複数回送っても結果が同じ(更新でもあり得る)
PUTは冪等であることが多い一方、更新の意味を持つので安全ではありません。取得をPUTに寄せると、「周辺がPUT=更新として扱う」前提と衝突します。
取得をPUTにすると何が困るのか(“動くのにダメ”の正体)
周辺機器はHTTPメソッドの意味で最適化している
HTTPはレイヤードシステム(中間装置が挟まる設計)を前提にしていて、GET/HEADは取得として扱われやすく、キャッシュや最適化の恩恵を受けやすいです。PUTで取得をすると、次のようなことが起きます。
- CDNやリバースプロキシが「PUTのレスポンスは基本キャッシュしない」扱いになり、帯域・レイテンシ改善が効かない
- 条件付き取得(ETag / If-None-Match、If-Modified-Since)などの取得系の作法が活きにくい
- 取得系のリクエストが増えるほど、インフラコストが“無駄に”上がる
プロフィールのように「頻繁に表示される」「内容は頻繁には変わらない」「でもユーザーごとに違う」というデータは、キャッシュ戦略(少なくともブラウザ・プロキシ・サーバー側の条件付き取得)と相性が良い領域です。PUTでその道を塞いでしまうと、後から性能改善したいときに選択肢が狭まります。
リトライやタイムアウト時の挙動が変わる(地味に痛い)
実運用では、ネットワーク断や一時的な502/503、タイムアウトは必ず起きます。そこでクライアントやゲートウェイは「このリクエストは安全に再送できるか?」を暗黙に判断します。
- GETは取得なので、一定条件で自動リトライの対象にしやすい
- PUTは更新の可能性があるため、むやみにリトライすると“二重更新”や想定外の副作用が起きると考えられ、リトライ対象から外されがち
あなたのAPIがPUTで取得をしていると、周辺が「更新っぽいからリトライしない」扱いになり、結果として取得の成功率が下がる・ユーザー体験が悪化する、という逆転現象が起きます。今は小さな違いでも、ユーザー数が増えるほど差が出ます。
ブラウザ環境ではCORSプリフライトが増えやすい
フロントエンド(SPA)や別ドメインのWebアプリからAPIを叩くと、CORSの影響を受けます。GETは条件を満たせば“シンプルリクエスト”として扱われやすい一方、PUTはプリフライト(OPTIONS)が発生しやすいです。
プリフライトが増えると、次のデメリットが積み上がります。
- 通信回数が増え、体感速度が落ちる(特にモバイルや海外回線)
- API GatewayやWAFの設定不備でOPTIONSだけ失敗し、原因が分かりづらい不具合になる
- 障害時に「本体は生きているのにプリフライトだけ落ちてログインできない」などの事故が起きる
プロフィール取得のような“最も頻繁に呼ばれる軽量なAPI”ほど、余計な往復は避けた方が得策です。
WAF・ファイアウォール・社内ネットワークでPUTが弾かれることがある
現場でありがちな“後から困る”例として、ネットワークやセキュリティ製品のポリシーがあります。
- 社内プロキシや古いネットワーク機器がPUT/DELETEを制限している
- WAFが「PUTは更新系だから厳しめに検査する」ルールになっている
- API Gatewayがメソッドごとにレート制限や監査ログの出し分けをしている
GETで取る設計ならスムーズに通る環境でも、PUTを使った瞬間に「環境によって動かない」という問題が出ます。開発環境では再現しにくく、本番・顧客環境・一部拠点だけで壊れるタイプの不具合になりがちです。
監視・ログ・監査が歪む(運用チームが困る)
運用では「参照系(GET)がどれだけ呼ばれたか」「更新系(POST/PUT/PATCH)がどれだけ失敗したか」を分けて見ます。PUTで取得していると、次のように意味が崩れます。
- 更新系メトリクスが不自然に増え、アラートがノイズ化する
- 監査ログで「更新が行われたように見える」ため調査コストが増える
- アクセス解析や課金(リクエスト単価)で誤った最適化判断をしやすい
特にプロフィール取得はアクセスが多いので、PUTでやると“全体の数字”を汚します。数字が汚れると、現場は意思決定を誤ります。メソッドを正しく使うことは、運用データを正しく保つことでもあります。
OpenAPI(Swagger)やSDK生成が不自然になる
ASP.NET CoreではOpenAPI(Swagger)を出すケースが多いはずです。OpenAPIや各種クライアント生成ツールは、HTTPメソッドの意味を前提にクライアントAPIを構築します。
- GETのエンドポイントは“取得メソッド”として生成され、キャッシュやリトライの設計が取りやすい
- PUTは“更新メソッド”として生成され、入力モデルやエラー処理の前提が変わる
- ドキュメント上でも「PUTでプロフィール取得」と書かれると違和感が強く、利用者の誤解を誘発する
APIの利用者が社内だけでも、半年後の自分や別チームが「PUT=更新」前提で触り、誤操作や無駄な調査が増えます。
| 困ること | 発生しやすいタイミング | 具体的な影響 | よくある原因 |
|---|---|---|---|
| キャッシュや最適化が効かない | アクセス増・表示速度改善が必要になったとき | レスポンス遅延、帯域/コスト増 | CDN/プロキシがGET前提で設計されている |
| CORSプリフライト増 | SPA化、別ドメイン化、モバイルWeb対応 | 通信回数増、OPTIONSだけ失敗 | PUTがシンプルリクエストになりにくい |
| ネットワーク制限で弾かれる | 顧客環境、社内拠点、ゼロトラスト導入 | 一部環境だけ動かない、再現困難 | PUT/DELETEを制限するポリシー |
| 監視・監査・ログ分類が崩れる | 運用開始、セキュリティ監査、障害調査 | 調査コスト増、誤アラート | 更新系=PUTという暗黙前提 |
| SDK/ドキュメントが不自然 | 外部公開、別チーム利用、保守フェーズ | 誤解、誤実装、問い合わせ増 | OpenAPIがメソッド意味で生成される |
「CRUDを守らなくても動く」のに、メソッドを使い分けるべき本当の理由
結論を短く言うなら、HTTPメソッドは“互換性のための共通言語”だからです。
アプリは、サーバーとクライアントだけで閉じていません。間に入るものは増え続けます。
- ロードバランサー
- API Gateway
- WAF
- CDN
- 監視/APM
- 自動テスト、契約テスト
- OpenAPIとSDK生成
- ブラウザの仕様(CORS、プリフライト、キャッシュ)
これらはHTTPメソッドの意味に依存して動きます。つまり、メソッドの意味を崩すと「自分のコード以外のところ」で損をします。逆に言えば、メソッドを守るほど“無料で使える最適化”が増えます。
プロフィール取得はどう設計するのが正解か(推奨エンドポイント例)
ユーザープロフィールは典型的なリソースです。取得と更新を分けるのが自然で、読み手・ツール・運用すべてが理解しやすくなります。
| やりたいこと | 推奨メソッド | 例 | ポイント |
|---|---|---|---|
| 自分のプロフィール取得 | GET | /users/me または /profile | 認証情報はAuthorizationヘッダーでOK |
| 自分のプロフィール全体を置換更新 | PUT | /users/me | “全部送って全部置き換える”ときに使う |
| 自分のプロフィール一部だけ更新 | PATCH | /users/me | 差分更新。送る項目だけ変更する |
| 他人のプロフィール(管理者・公開ページなど) | GET | /users/{id} | 権限や公開範囲に応じて制御 |
「JWTだからGETにできない」という心配はよく聞きますが、GETでもAuthorizationヘッダーにBearerトークンを載せるのは一般的です。GETにすることで情報がURLに出るわけではありません(トークンをクエリに載せない設計にします)。
リクエスト例(GETでプロフィールを取得)
GET /users/me HTTP/1.1
Host: api.example.com
Authorization: Bearer {JWT}
Accept: application/json
レスポンス例
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "u_123",
"displayName": "Taro",
"email": "[[email protected]](mailto:[email protected])",
"updatedAt": "2025-12-31T10:00:00Z"
}
ASP.NET Coreでの実装イメージ(ルーティングと責務を分ける)
ASP.NET Coreでは、メソッドごとにアクションを分けると意図が明確になります。
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("users")]
public class UsersController : ControllerBase
{
// GET /users/me
[Authorize]
[HttpGet("me")]
public IActionResult GetMe()
{
// 認証済みユーザーのプロフィールを取得して返す
return Ok(new { /* ... */ });
}
// PUT /users/me
[Authorize]
[HttpPut("me")]
public IActionResult PutMe([FromBody] UpdateProfileRequest request)
{
// プロフィールを置き換え更新する
return NoContent();
}
// PATCH /users/me
[Authorize]
[HttpPatch("me")]
public IActionResult PatchMe([FromBody] PatchProfileRequest request)
{
// プロフィールを部分更新する
return NoContent();
}
}
ポイントは「GETは参照」「PUT/PATCHは更新」という責務がコード上でも一目で分かることです。レビューも運用も迷いません。
GETにするとキャッシュで情報漏洩しない?(個人情報APIの現実的な対策)
プロフィールは個人情報を含むことが多いので、「GETはキャッシュされるから危険では?」という不安も出ます。ここは“正しい対策”を取るのが重要です。メソッドをPUTに変えても、本質的な安全性が上がるわけではありません。
基本は「キャッシュさせない」ヘッダーを付ける
個人情報や認証済みのユーザー固有データは、原則としてキャッシュさせない方が安全です。代表的には次のような設定をします。
- Cache-Control: no-store(保存しない)
- Pragma: no-cache(古いクライアント向け)
これによりGETでも不要な保存を抑えられます。加えて、CDNやリバースプロキシを使う場合は「Authorizationヘッダーが付くレスポンスはキャッシュしない」設定や、Varyヘッダーの扱いを確認します。
それでも性能が必要なら「条件付き取得」で帯域を削る
キャッシュを完全に許可しなくても、ETagとIf-None-Matchによる条件付き取得で、変更がないときは304 Not Modifiedにしてデータ転送量を削れます。これは「GETで取得する」設計の王道です。PUTで取得していると、この設計が一気に不自然になります。
「GETはボディを送れない」問題があるときの正しい逃げ道
取得系でも、検索条件が複雑でクエリパラメータに載せにくいことがあります。例えば、以下のようなケースです。
- 条件が多く、URLが長くなりすぎる
- 条件がネスト構造で表現しづらい(複数フィルタ、範囲条件、ソート、集計)
- 機密性の高い検索条件をURLに出したくない(ただしログや監査との整合も必要)
この場合、実務的にPOSTで検索(クエリ実行)を作ることはあります。ここで大事なのは「PUTで取得」ではなく、POSTを“検索処理”として割り切ることです。
| やりたいこと | よくある設計 | メリット | 注意点 |
|---|---|---|---|
| 単純な取得 | GET /resources?filter=… | キャッシュ・条件付き取得が使いやすい | URL長・表現力に限界 |
| 複雑検索(ボディが必要) | POST /resources/search | ボディで柔軟に条件を渡せる | GETほどキャッシュの恩恵が少ない |
| 検索条件をリソース化 | POST /searches → GET /searches/{id} | 結果の再取得や共有がしやすい | 実装は少し重くなる |
「取得系なのにPOST」は“例外的に合理性がある”一方で、PUTを取得に使うのは、更新の意味とぶつかって例外としても合理性が薄いのがポイントです。
PUTで取得してしまっているAPIを、現場で安全に直す移行手順
すでにクライアントがPUTでプロフィール取得を呼んでいる場合、いきなり消すと事故になります。現実的には段階的に移行します。
| ステップ | やること | 狙い | よくある落とし穴 |
|---|---|---|---|
| GETを追加 | GET /users/me を新設し、同じレスポンスを返す | 新しい正規ルートを用意 | レスポンス形式がズレると移行が進まない |
| ドキュメントを更新 | OpenAPI/SwaggerをGET中心にし、PUT取得は非推奨扱い | 利用者の行動を変える | 口頭だけだと残り続ける |
| 警告を出す | PUT取得のレスポンスにWarningヘッダー等で非推奨を通知(またはログで検知) | 移行の圧力をかける | 警告が見られない設計だと効果が薄い |
| 計測して期限を切る | PUT取得の利用率を計測し、段階的に停止計画を立てる | 安全に廃止 | “誰が使っているか”を追えないと止められない |
| 最終的に405へ | PUT取得を廃止し、Method Not Allowed + Allowヘッダーを返す | 仕様として明確化 | 突然やると顧客影響が出る |
リダイレクト(301/302/307/308)でPUT→GETに誘導するのは、クライアント側の挙動がまちまちで安全ではありません。HTTPメソッドが絡む移行は、基本的に「新ルート追加→告知→計測→廃止」が堅いです。
よくある誤解をまとめて潰す(GET/PUTの設計で迷いがちな点)
誤解:GETはURLに情報が出るから危険
危険なのは「トークンや機密情報をクエリに載せる設計」です。GETでもAuthorizationヘッダーにBearerトークンを載せればURLに露出しません。さらにHTTPSは必須です。
誤解:PUTは冪等だから“読み取り専用”でも問題ない
冪等は「同じ操作を繰り返したとき結果が同じ」という性質で、読み取り専用を意味しません。周辺はPUTを更新として扱うので、読み取りに使うほど衝突が増えます。
誤解:GETは勝手にキャッシュされるからプロフィールには向かない
キャッシュ制御ヘッダーで“保存させない”設計ができます。GETをやめるのではなく、GETを使った上でキャッシュ制御を正しくします。
誤解:取得でもボディを送りたいからPUTにした
ボディが必要なら、PUTではなくPOSTで検索・計算処理として設計する方が現実的です。PUTは置換更新の意味と密接で、取得に使う合理性が薄いです。
GETに統一するときのチェックリスト(プロフィールAPI向け)
- エンドポイントは GET /users/me など、意図が一目で分かる形にする
- 認証はAuthorizationヘッダーで統一し、クエリにトークンを載せない
- 個人情報なら Cache-Control: no-store を基本にし、必要なら条件付き取得(ETag)を検討
- 更新はPUT(置換)/PATCH(部分更新)で責務分離する
- OpenAPI/Swaggerを正しいメソッドで整備し、SDK生成や利用者の誤解を減らす
- 移行が必要なら、GET追加→告知→計測→405の順で安全に進める
まとめ:今は動いても、標準の意味から外れるほど“将来の支払い”が増える
PUTでプロフィール取得が動くのは、サーバーがそう実装しているからです。しかしHTTPメソッドの意味を崩すと、キャッシュ・中間装置・CORS・監視・WAF・SDK生成といった周辺が想定通りに動かず、問題がジワジワ積み上がります。
プロフィール取得はGET、更新はPUT/PATCH。ここを正しく分けるだけで、相互運用性と保守性が上がり、性能改善や運用の選択肢も増えます。結果として「後から困らないAPI」になります。

コメント