問題の概要
APIのコールバック関数で、本来POSTメソッドで呼び出されるはずの処理がGETメソッドで実行され、500エラーが発生する問題に遭遇した。一見すると謎の現象だが、原因を調査すると意外なユーザー行動が関係していた。
発生した症状
- APIコールバック処理で500エラーが発生
- 正常に動作する場合と異常な場合が混在
- エラーログを確認すると、POSTで処理されるべき箇所がGETメソッドで実行されている
原因の調査
初期の仮説
最初は以下のような原因を疑った:
- APIプロバイダー側の仕様変更
- サーバー側のルーティング設定ミス
- リダイレクト処理での変換
真の原因
詳細な調査の結果、以下の流れで問題が発生していることが判明:
- ユーザーがAPIを呼び出し、正常にPOSTでコールバックが実行される
- ユーザーがブラウザの戻るボタンを押下
- ブラウザが前のページに戻る際、再度コールバックURLにアクセス
- この時、ブラウザはGETメソッドでアクセス
- 古いコードに残っていた検証用のGETルートが実行される
- 不完全な処理により500エラーが発生
技術的な解説
なぜPOSTがGETに変わるのか
ブラウザの戻るボタンを押した際、以下の動作が発生する:
1. 最初のフロー(正常)
POST /api/callback → 正常処理 → リダイレクト → 完了ページ
2. ブラウザバック時のフロー(問題)
完了ページ → [戻るボタン] → GET /api/callback → エラー
ブラウザは履歴を辿る際、元のHTTPメソッドを保持せず、GETメソッドでアクセスする仕様になっている。
問題のコード例
// 古いコード(問題の原因)
app.post('/api/callback', (req, res) => {
// 正常な処理
processCallback(req.body);
res.redirect('/success');
});
// 検証用として残っていたGETルート
app.get('/api/callback', (req, res) => {
// 不完全な検証処理
validateCallback(req.query); // ここで500エラー
});
対処法
1. 不要なGETルートの削除
// 検証用のGETルートを削除
// app.get('/api/callback', ...); // 削除
app.post('/api/callback', (req, res) => {
processCallback(req.body);
res.redirect('/success');
});
2. GETアクセスへの適切な対応
app.get('/api/callback', (req, res) => {
// 適切なエラーレスポンスまたはリダイレクト
res.status(405).json({
error: 'Method Not Allowed',
message: 'This endpoint only accepts POST requests'
});
});
3. 冪等性を考慮した設計
app.post('/api/callback', (req, res) => {
const transactionId = req.body.transaction_id;
// 既に処理済みかチェック
if (isAlreadyProcessed(transactionId)) {
return res.redirect('/success');
}
processCallback(req.body);
markAsProcessed(transactionId);
res.redirect('/success');
});
4. フロントエンド側での予防策
// ブラウザバックを無効化(必要に応じて)
window.history.pushState(null, null, window.location.href);
window.addEventListener('popstate', function(event) {
window.history.pushState(null, null, window.location.href);
});
学んだ教訓
1. ユーザー行動の想定
- ユーザーは予期しない操作を行う可能性がある
- ブラウザの基本機能(戻るボタン等)への考慮が必要
2. 古いコードの管理
- 検証用やテスト用のコードは適切に削除する
- 不要なルートを残さない
3. エラーハンドリングの重要性
- 想定外のHTTPメソッドでのアクセスにも適切に対応
- 明確なエラーメッセージで問題の特定を容易にする
4. 冪等性の設計
- 同じ処理が複数回実行されても安全になるよう設計
- 重複処理の検出と適切な処理
まとめ
APIコールバック処理では、ユーザーのブラウザ操作によって予期しないHTTPメソッドでアクセスされる可能性がある。特にブラウザの戻るボタンによる操作は、POSTリクエストをGETリクエストに変換してしまう。
この問題を防ぐには:
- 不要なルートの削除
- 適切なエラーハンドリング
- 冪等性を考慮した設計
- ユーザー行動の想定