【Node.js】POST後にページリロードすると再度POSTされる問題
2019年はプレイベートでサービスを作りたいので、Node.jsの勉強を始めました。
サーバーサイドプログラミングの経験はほとんどないので、まずは入門書から始めているのですが、そのサンプルアプリケーション(掲示板)の中で問題が発生しました。
その内容は、ググっていると全く同じことを書いていた記事がqiitaにありました(しかも同じ入門書)。
コメントをたどっていくと、どうやらPOST後はリダイレクトさせるPRGパターンというのが定石のようでした。
今回はステータスコード 303 See Other
でリダイレクトさせるのが正しいことが分かりました。
HTTPの仕様を決めているRFC 2616を読むと,最も適しているのは303であることがわかります。このステータスコードは「情報は指定した別のURLにあるので,GETでアクセスしろ」というステータスで「POSTでのアクセスに対して別のURLをGETで読み込む」ために用意されたものです。ということで,まさにぴったりです。一方,301と302はというと,301は「恒久的にURLが変更されたこと」を,302は「一時的にURLが変更されたこと」を示すステータスコードですから,目的とはちょっと違っています。
(第28回 フォーム送信とブラウザ・ボタンと使い勝手(前編)~PRGパターンをご存じですか(2ページ目) | 日経 xTECH(クロステック) より)
コードを修正
修正前
// データ受信終了のイベント処理 request.on('end', () => { // 省略 response.writeHead(200, { 'Content-Type': 'text/html' }); response.write(/* 省略 */); // indexページを表示させています response.end(); });
修正後
// データ受信終了のイベント処理 request.on('end', () => { // 省略 response.writeHead(303, { 'Location': '/' }); // indexページに303リダイレクト response.write(/* 省略 */); response.end(); });
これにより、無事リロードしても二重でPOSTされることはなくなりました。
今回は簡易的な掲示板だったのでこれで大丈夫かと思いますが、ECサイトなどで二重注文が発生しては絶対にいけないような場合は、ワンタイムトークンを発行したりセッションIDを使って厳密に判定を行う必要があるようです。
さらにリダイレクトさせる理由として大きなポイントは、
POSTメソッドによってアクセスされたページを再読込すると,ブラウザは「もう一度情報をPOSTしちゃうけどいい?」という以下のようなダイアログを表示してしまうからです」
ということが挙げられていました。
さらに以下のように続きます。
リダイレクトを挟むことで,再読込をしても,単にGETのアクセスが発生するだけの状況を作り,いちいちメッセージが表示されてしまうことを防ぐことができます。
...(略)
PRGパターンというのは,POSTの結果として表示したいページが再読込みされやすい場合に,特に有効だといえます。
...(略)
つまりPRGパターンを利用するかどうかは,そのページが再読み込みされるかどうかが判断ポイントとなりそうですね。
なるほど。。。
サーバーサイドではこのようなことも考える必要があるのですね。大変勉強になったお話でした。