5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

postで叩かれるはずのルートがgetで叩かれていた謎を解明したので残しておく。

Posted at

問題の概要

APIのコールバック関数で、本来POSTメソッドで呼び出されるはずの処理がGETメソッドで実行され、500エラーが発生する問題に遭遇した。一見すると謎の現象だが、原因を調査すると意外なユーザー行動が関係していた。

発生した症状

  • APIコールバック処理で500エラーが発生
  • 正常に動作する場合と異常な場合が混在
  • エラーログを確認すると、POSTで処理されるべき箇所がGETメソッドで実行されている

原因の調査

初期の仮説

最初は以下のような原因を疑った:

  • APIプロバイダー側の仕様変更
  • サーバー側のルーティング設定ミス
  • リダイレクト処理での変換

真の原因

詳細な調査の結果、以下の流れで問題が発生していることが判明:

  1. ユーザーがAPIを呼び出し、正常にPOSTでコールバックが実行される
  2. ユーザーがブラウザの戻るボタンを押下
  3. ブラウザが前のページに戻る際、再度コールバックURLにアクセス
  4. この時、ブラウザはGETメソッドでアクセス
  5. 古いコードに残っていた検証用のGETルートが実行される
  6. 不完全な処理により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リクエストに変換してしまう。

この問題を防ぐには:

  • 不要なルートの削除
  • 適切なエラーハンドリング
  • 冪等性を考慮した設計
  • ユーザー行動の想定
5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?