🏹

新人Webエンジニア必須?の知識「PRGパターン」について

2021/04/01に公開

はじめに

PRGパターン聞いたことありますか?多分、2010年ごろまではよく語られていた印象ですが、年月を経るにつれ聞かなくなったなぁと、ふと思いました。その理由としては、今の開発において不要になったからという訳では無く、むしろ当たり前になったからだと思います。

なのでベテランの人がこのパターンについての説明を聞くと、なんだそんなことか!って思うかもしれません。一方で、いわゆる駆け出しエンジニアに分類されるような初学者の人は、これを意識することで今後のWeb開発時の実装における苦労を軽減できるものと思います。

なお、この話のスコープにSPAは含まれておりません。

PRGパターンとは何か?

PRGパターンは、Post Redirect Getの頭文字を繋いだものになります。これは、POSTメソッドでリクエストを受け取った場合、そのメソッドの処理の終わりに必ずRedirectし、処理後の結果はGetメソッドで呼び出せる画面に返しましょうというデザインパターンです。

何が嬉しいか?

POSTメソッドのリクエストを受けた後、そのままviewをreturnした場合、以下の3つの嬉しくない挙動が生じます。

  1. return後の画面をリロードしようとすると、再度フォームを再送信する旨の確認ダイアログが表示され、OKを押すとそのままPOSTが実行されてしまう。
  2. return後の画面からブラウザの「戻る(history back)」で前の画面に戻ろうとした場合、再度フォームを再送信する旨の確認ダイアログが表示され、OKを押すとそのままPOSTが実行されてしまう。
  3. 表示する画面のURLが1:1で紐づかなくなる。例えばECサイトでカート登録後にカート内の一覧を表示しようとした場合、従来の一覧ページとは異なるURLで表示される。また登録処理後に一覧用のデータ取得の処理を追加する必要がある。

デメリットは無いのか?

基本的には無いです。ただ強いてあげるなら、ブラウザの「戻る」ボタンから戻ると、入力した値が残っているため、そこで再度POST用のアクションを実行すると、二重POSTができてしまうことでしょうか。

その辺りは、レガシーなフレームワークならCSRF対策の副作用(トークンを再生成するので2回目のPOSTはCSRFエラーになる)によって防止できたりしますが、私が愛用しているLaravelのCSRF対策には副作用が無い(トークンを再生成しないので2回目のPOSTできちゃう)ので、別途対策を用意する必要があります。ただこれは毛色が違う話だと思うので、本記事ではスルーしたいと思います。

ケーススタディ

登録完了

PRGパターンの一番典型的なケースが、登録処理後の完了ページへのリダイレクトでしょう。こうすることで、不用意な二重送信を防ぐことができます。

しかし、最近はRESTfulAPIをベースにしたURL設計が幅を効かせているので、リダイレクトしてしまうと適切なリダイレクト先のURLがRESTfulAPIの原則にそぐわないという問題に直面します。その場合、原則からあえて逸脱し/complete/finishといったURLを追加し、showメソッドを呼び出して完了用の表示に分岐するという対応になるかと思います。

バリデーションエラー

フォームからの入力値を受け付けた後、バリデーションエラーになった場合にリダイレクトするかは議論が別れるケースです。

Laravelに関してはリダイレクトするよう実装されていますが、もともとのPRGパターンではあくまで処理が成功した場合にリダイレクトするというケースのみの言及に止まっています。このため例えばCakePHPでは、Laravelと同じくエラー時にもPRGパターンで返しましょうという提案に対して、典型的なPRGパターンに違反するとの指摘が入りリジェクトされています。

なおリダイレクトする場合は、各項目に対応するエラーメッセージを渡す必要が生じます。そこで、メッセージを各項目の名前と対応する形で変数に詰め、セッションに一時的に格納してからリダイレクトする形になります。その上で、画面を描画する際にセッションからそれらのメッセージを取り出し表示します。

おわりに

PRGパターンについては以上です。この記事を書くにあたって、最近は本当に言及されていないのかを調べたところ、Qiitaでは2019年に書かれたPost/Redirect/Get (PRG) パターンという記事がLGTMを100以上を獲得していました。また海外に目を向けると、プログラミング学習者用のサイトにPRGパターンに関するコラムが掲載されるというケースは見つかりました。なので、今でも十分使えるデザインパターンであると言えるでしょう。

関連資料

もう少し詳しく知りたいという方のために、PRGパターンに言及している資料を紹介しておきます。

第28回 フォーム送信とブラウザ・ボタンと使い勝手(前編)~PRGパターンをご存じですか
https://xtech.nikkei.com/it/article/COLUMN/20071112/286994/

Teeda Extension:PRGパターン
http://teeda.seasar.org/ja/extension/concept/prg.html

PRGパターンをPHPで実装してみる
https://blog.j5ik2o.me/entry/20070301/1172734267

Post/Redirect/Get
https://en.wikipedia.org/wiki/Post/Redirect/Get

Redirect After Post
https://www.theserverside.com/news/1365146/Redirect-After-Post

Discussion