POST メソッドの多重送信を防ぐ PRG パターンとは
POST リクエストには、データを作成したり編集したりする処理を割り当てることが多いです。 このようなデータの更新処理は、同じリクエストを複数回実行しないように対策するのが定石となっています。
意図しない POST リクエストの再送信は、ブラウザのリロードボタンをクリックしたときに発生します。 PRG パターンは、この問題を解決するための方法です。 このレクチャーでは、PRG パターンが解決する問題を解説し、Spring Framework での実装方法を紹介します。
目次
PRG パターンとは何か
PRG パターンとは、ブラウザでのリロードによって、POST リクエストが再送信される問題を解決するためのデザインパターンです。 PRG は、Post - Redirect - Get の頭文字をとった略語です。 名前の通り、HTTP の仕組みであるリダイレクトを活用します。
リダイレクトとは何か
HTTP のリダイレクトは URL 転送とも呼ばれます。 クライアントがリクエストした URL に対して、サーバーが別の URL を返すことを指します。
リダイレクトは、サーバーがリダイレクトレスポンスを返すことで実現されます。 リダイレクトレスポンスの特徴は以下の2つです
- レスポンスステータスコードが
3xx
である - レスポンスの
Location
ヘッダーに転送先の URL が指定されている
このレスポンスを受け取ったクライアントは、Location ヘッダに指定されている URL に対して GET リクエストを実行します。 この一連の流れによって、ある URL に対するリクエストを別の URL に転送することができます。 これをリダイレクトと言います。
PRG パターンの処理フロー
PRG パターンの処理フローを見てみましょう。 以下のように、Post - Redirect - Get の順に HTTP リクエストが実行されます。
- Post - ユーザーの操作によってフォームが送信されます
- Redirect - POST メソッドのレスポンスとしてリダイレクトレスポンスが返されます。Location ヘッダーにはフォームの送信後に表示するページが指定されます
- Get - リダイレクトによって、Location ヘッダーに指定されたページに遷移します
PRG パターンはなぜフォームの再送信を防げるのか
PRG パターンがなぜフォームの再送信を防げるのかを見てみましょう。 タスク管理アプリケーションを例に考えます。 このアプリケーションには以下のようなエンドポイントがあるとします。
POST /tasks
- タスク作成処理GET /tasks
- タスク一覧画面表示
PRG パターンを考える際には、ブラウザのリロードについての知識が重要です。 リロードとは、現在表示されているページを取得するときに実行したリクエストを再実行することです。
それでは、PRGパターンが適用されたアプリケーションと、適用されていないアプリケーションを見比べてみましょう。
Case #1:PRG パターンを適用していないアプリケーション
PRG パターンを適用していないアプリケーションでは、タスク作成後にブラウザをリロードすると以下のような挙動になります。
- タスク作成ボタンをクリックする。
POST /tasks
のリクエストが発行される - (
POST /tasks
のレスポンスとして200 OK
が返り)タスク一覧画面が表示される - ブラウザのリロードボタンをクリックする
- 直前のリクエスト
POST /tasks
が再実行される
このように、ブラウザのリロードによって POST /tasks
が再実行されてしまいます。
Case #2:PRG パターンを適用したアプリケーション
PRG パターンを適用したアプリケーションでは、タスク作成後にブラウザをリロードすると以下のような挙動になります。
- タスク作成ボタンをクリックする。
POST /tasks
のリクエストが発行される POST /tasks
のレスポンスとしてリダイレクトレスポンス302 Found
が返る。Location ヘッダーには/tasks
が指定されている- リダイレクトレスポンスを受け取ったブラウザは、
Location
ヘッダーに指定された/tasks
に対して GET リクエストを実行する - ブラウザのリロードボタンをクリックする
- 直前のリクエスト
GET /tasks
が再実行される
POST リクエストのあとにリダイレクトを差し込むことがポイントです。 こうすることで、リロード時に実行されるメソッドを GET リクエストに差し替えることができます。
PRGパターンの実装方法
PRG パターンを実装するには、リダイレクトレスポンスを返す必要があります。
Spring Framework でリダイレクトレスポンスを返す方法は簡単です。
コントローラーのハンドラーメソッドの戻り値を redirect:
から始め、続けて遷移先の URL を指定します。
タスク作成のリクエスト(POST /tasks
)を処理するハンドラーメソッドに PRG パターンを適用すると以下のようになります。
@PostMapping("/tasks")
public String creataTask(TaskForm form) {
// ... タスク作成処理 ...
// 302 Found (Location: /tasks) を返す
return "redirect:/tasks";
}
このハンドラーメソッドでは、POST /tasks
のレスポンスとして 302 Found
が返るように設定しています。
ハンドラーメソッドの戻り値に、redirect:/tasks
と書かれているところに注目してください。
これによって、
- レスポンスステータスコードが
302 Found
になり - レスポンスの
Location
ヘッダーに/tasks
が指定された
リダイレクトレスポンスがクライアントに返されます。
まとめ:POST リクエストを処理するハンドラーメソッドには PRG パターンを適用しよう
このレクチャーでは、PRG パターンを紹介しました。
- PRG パターンは、ブラウザのリロードによる多重送信を防ぎます
- PRG パターンでは、POST リクエストのあとにリダイレクトを実行します
- Spring では、コントローラーのハンドラーメソッドに
redirect:
から始まる戻り値を設定することでリダイレクトレスポンスを生成できます
フォームの多重送信を防ぐために、POST メソッドを処理するハンドラーメソッドには忘れずに PRG パターンを適用するようにしましょう。