概要
入力、確認、完了画面と遷移する、よくあるフォームを作っていて、uuidによるブラウザバック対策を試したのでメモ。
ブラウザバックそのものというよりは、完了から確認画面にブラウザバックして何度も投稿させるのを防ぐことを目的にしている。
方針
DBのテーブルに、uuidを保存するカラムを追加する。
すると完了画面の時点でuuidも保存されるが、パラメーターの中に残っているuuidがテーブル内に既に存在する場合は、エラー画面にリダイレクトさせる。
実装
1. カラム追加
uuidを保存するためのカラムを足す。
class AddUuidToSamples < ActiveRecord::Migration[5.2]
def change
add_column :samples, :uuid, :string
end
end
2. コントローラー
newアクションのタイミングで、uuid を発行しておく。confirm では、キャッシュを保存できないよう no-store を設定。
confirm と create で uuid が既に存在していないかを確認し、存在していればエラー画面に遷移させる。
Cache-Controlの参考
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Cache-Control
before_action :verify_validate, only: [:confirm, :create]
def new
@sample_form.uuid = ::SecureRandom.uuid
end
def confirm
response.headers['Cache-Control'] = 'no-store'
end
def create
if sample_form.save
redirect_to complete_sample_path
else
render :error
end
end
private
def sample_form
@sample_form ||= ::SampleForm.new(Sample.new)
end
def verify_validate
render :new sample_form.validate(params[:sample])
return redirect_to error_sample_path if Sample.exists?(uuid: sample_form.uuid)
end
3. ビュー
最後に、new と confirm に対応するビューに、hidden_fieldを仕込んでおく。
uuid の生成は、newアクションだけで行うようにして、以降は hidden_field でバケツリレーさせる。
= form_for sample_form, url: confirm_samples_path do |f|
# 省略
= f.hidden_field :uuid
= f.button '確認'
= form_for sample_form, url: confirm_samples_path do |f|
# 省略
= f.hidden_field :uuid
= f.button '送信'