2024年3月28日木曜日

ページの再ポストを防止する仕組みについて

Oracle APEXのアプリケーションに含まれるページのプロパティに、ページの重複送信の許可があります。選択できる値は、はい - ページの再ポストを許可するまたは、いいえ - ページの再ポストを防止する、です。

この設定はページ・プロパティ詳細に含まれます。デフォルトは、はい - ページの再ポストを許可する、です。


ページの再ポストを防止する仕組みは、以下のように実装されています。

Oracle APEXの組込み置換文字列としてAPP_UNIQUE_PAGE_IDがあります。ドキュメントには以下のように説明されています。

APP_UNIQUE_PAGE_IDは、各ページ・ビューに固有なOracle順序から生成される整数です。この数値は、アプリケーションでの重複ページの送信を防止するために使用され、その他の用途にも使用できます。

APEXが生成する全てのページに、このAPP_UNIQUE_PAGE_IDが含まれていて、ページの送信時に同時に送信されます。非表示のパラメータp_page_submission_idの値がそれになります。


ちなみに非表示のパラメータp_flow_idアプリケーションID(APP_ID)p_flow_step_idページID(APP_PAGE_ID)p_instanceセッションID(APP_SESSION)が割り当たります。これらはOracle APEXの開発が始まった当初の名前がFlowsだったことに由来しています。(英語版WikipediaのOracle Application ExpressのBackgroundを参照のこと)。

ページの重複送信の許可いいえ - ページの再ポストを防止するになっていると、APP_UNIQUE_PAGE_IDを対象としたページの送信が1度だけしか行えないように制限されます。

簡単なAPEXアプリケーションを作成して、動作を確認してみます。

そのページのAPP_UNIQUE_PAGE_IDを表示、または、p_page_submission_idに設定する値を指定するページ・アイテムP1_APP_UNIQUE_PAGE_IDを作成します。ページを送信するボタンSUBMITp_page_submission_idにページ・アイテムP1_APP_UNIQUE_PAGE_IDの値を設定するボタンREPLACEを作成します。


ページ・アイテムP1_APP_UNIQUE_PAGE_IDは、識別タイプテキスト・フィールドとします。

ソースタイプアイテムを選択し、アイテムとしてAPP_UNIQUE_PAGE_IDを指定します。セッション・ステートストレージリクエストごと(メモリーのみ)を選択します。

ページがロードする際に、APP_UNIQUE_PAGE_IDの値がページ・アイテムに設定されます。


ボタンSUBMITは、アクションとして単にページの送信を行います。


ボタンREPLACEには動的アクションを作成し、ボタンをクリックしたときに以下のJavaScriptコードを実行するようにします。

ページ・アイテムP1_APP_UNIQUE_PAGE_IDに指定されている値を、IDがpPageSubmissionIdのINPUT要素(タイプはhidden)に設定しています。

$s("pPageSubmissionId", apex.items.P1_APP_UNIQUE_PAGE_ID.value);


ページ・プロパティのページの重複送信の許可いいえ - ページの再ポストを防止するに設定します。


アプリケーションを実行します。

ボタンSUBMITをクリックすると、APP_UNIQUE_PAGE_IDは毎回更新されます。


APP_UNIQUE_PAGE_IDの値をクリップボードにコピーします。

再度、ボタンSUBMITを複数回クリックします。


現在のAPP_UNIQUE_PAGE_IDの値を送信済みのAPP_UNIQUE_PAGE_IDに置き換え、ボタンREPLACEをクリックします。


ボタンSUBMITをクリックすると再ポストの防止が働き、「このページはすでに送信されているため、再送信できません。」とエラーが表示されます。


つまり、ページ送信時のパラメータp_page_submission_id(type=hiddenのINPUT要素 - idはpPageSubmissionId)として渡されるAPP_UNIQUE_PAGE_IDが以前に送信済みであれば、再ポストの防止が設定されているページでは、エラーが発生します。

ページの送信を行なうコードapex.page.submitの内容を確認すると、ページの再描画が行われる以前に、HTTPのPOSTの応答を受け取った時点でp_page_submission_idは更新されています。


現実的な状況としては、送信したページのレスポンスを受け取る前(p_page_submission_idが変わる前)に再度ページを送信した時に、エラーが発生すると言えます。

簡単なアプリケーションですがエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/prevent-page-repost.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。