複数のフォームを同一画面・複数画面で扱う際に起こりやすい「競合」「衝突」「セッション不整合」などの問題を防ぐための設計と実装方針を、Struts2の視点から整理して解説します。
✅ 1. なぜ複数フォームはトラブルを招くのか?
☑ 例:同一画面に複数フォームがある場合
<!-- 検索フォーム -->
<s:form action="searchUser">
<s:textfield name="keyword" label="キーワード" />
<s:submit value="検索" />
</s:form>
<!-- 新規ユーザ登録フォーム -->
<s:form action="createUser">
<s:textfield name="username" label="ユーザ名" />
<s:textfield name="email" label="メールアドレス" />
<s:submit value="登録" />
</s:form>
🔥 起こりうる問題
-
どちらのフォームでエラーが起きたのか特定しづらい
-
入力チェックが他のフォームにも影響を与える
-
セッションに一時保存していたデータが他のフォーム操作で上書きされる
✅ 2. 対策①:トークンでの二重送信防止(tokenInterceptor)
Struts2には tokenInterceptor
が用意されており、同じフォームを2度送信することを防ぐ
のに有効です。
<s:form action="createUser">
<s:token />
<s:textfield name="username" label="ユーザ名" />
<s:submit value="登録" />
</s:form>
<action name="createUser" class="CreateUserAction">
<interceptor-ref name="token"/>
<interceptor-ref name="defaultStack"/>
<result name="invalid.token">duplicateSubmit.jsp</result>
<result name="success">success.jsp</result>
</action>
🔐 注意:
複数のフォームで を使うと、同じセッション内で最後のトークンだけが有効になるため、
セッションに依存せず、JavaScriptで動的に送信先を切り替える方式も併用を検討します。
✅ 3. 対策②:フォーム単位での専用Action+専用Formクラス設計
❌ NGパターン:
public class UserAction extends ActionSupport {
private String keyword; // 検索用
private String username; // 登録用
private String email; // 登録用
// フォームを兼ねたフィールドが混在
}
✅ OKパターン:
-
SearchUserAction(検索専用)
-
CreateUserAction(登録専用)
Actionクラスごとにフォームを完全に分離し、1アクション=1フォームの原則を守ることで、
フィールド競合・バリデーションの混乱を防止できます。
✅ 4. 対策③:セッション一時保存はスコープと命名を厳密に管理
☑ よくあるバグの元:
session.put("userInput", form);
→ 複数画面で同じキー名 "userInput"
を使うと、他フォームの入力が上書きされる
✅ 改善例:
session.put("createUserForm", form);
session.put("editUserForm", form);
または、フォームの状態管理用にForm IDやタイムスタンプを付ける方法もあります。
✅ 5. 対策④:動的フォーム生成時はhidden+ID管理で状態を持つ
JavaScriptなどでフォームを動的に増やす場合:
<form action="editUser">
<input type="text" name="username" />
<input type="hidden" name="formId" value="create-1" />
</form>
サーバ側で formId
を受け取り、セッション上のどのフォーム状態と対応するかを判断します。
✅ 6. よくあるケース:画面A → B → Cで途中セッション消える問題
セッションを多用したステップ式フォームの場合、別画面のアクセスによりセッションが破棄・上書きされることがあります。
✅ 対策:
- 状態管理をセッションでなくDBやクッキーに移す
- セッション内でも、画面単位でオブジェクト名を完全分離(例:
createUser.step1
,createUser.step2
)
✅ 7. テスト時に注目すべきポイント
-
タブを複数開いて操作してみる
-
バリデーションエラー後に戻って入力できるか
-
戻るボタン・リロードで意図しないデータが送信されないか
-
セッションタイムアウト後の動作確認
🎯 まとめ
課題 | 解決策 |
---|---|
フォーム同士の競合 | Actionクラス・Formクラスの分離 |
二重送信 |
<s:token /> + tokenInterceptor
|
セッション上書き | セッションキーを画面単位にする |
フォーム状態の混乱 |
formId をhiddenで渡す |
多画面セッション競合 | セッション管理設計の明確化 or DB化 |
▶️ 次回予告:Vol.11.21 JSON返却の基本とStruts2 REST連携の仕組み
JSONベースのAPIレスポンスや、RESTful構成への第一歩として、Struts2での stream
や json
タイプの使い方と実例を解説します。
🧭 関連記事(Vol.11.xxxシリーズ)
連番 | タイトル | 内容分類 |
---|---|---|
Vol.11.0 | Struts2が提供してくれる便利機能まとめ9選 | 導入&概要 |
Vol.11.1 | 自動注入とステートレスActionの仕組み | フレームワーク内部理解 |
Vol.11.2 | UX改善に効く!アクション層設計と入力制御の極意(プロローグ) | UX設計・アクション層総論 |
Vol.11.2.1 | 【実践編】Struts2アクション層の役割と設計パターン(Command型 / UIアクション分離など) | アクション設計パターン |
Vol.11.2.2 | 【UX重視】戻るボタン・キャンセル処理の設計手法と「戻り先管理」のスマート実装 | 戻り先制御とUX設計 |
Vol.11.2.3 | 【設計ノウハウ】多画面遷移時のパラメータ受け渡し・保持戦略(セッション vs hidden vs URL) | パラメータ多重管理 |
Vol.11.2.4 | 【実践ガイド】ユーザー操作を考慮したアクション遷移設計(リダイレクト vs フォワードの使い分け) | UX遷移設計/Result制御 |
Vol.11.2.5 | 【トラブル防止】アクション層の共通処理とメンテナブルなBaseAction設計 | アクション共通化/保守性 |
Vol.11.3 | Struts2の defaultStack とは?Interceptorの中身を詳しく追ってみる | Interceptor |
Vol.11.3.2 | 🔧 Vol.11.3.2 独自Interceptorの作り方と使い所 Struts2のカスタマイズを“実践で使えるレベル”に一歩進めよう | Interceptor |
Vol.11.4 | 入門者のための「パラメータ自動バインディング」完全理解ガイド | 自動バインディング |
Vol.Vol.11.4.1 | 入門者のための自動バインディング実践パターン集 | 自動バインディング |
Vol.11.5 | Struts2の構成理解が“実務レベル”へと進化する決定版 | 設定ファイル構造 |
Vol.11.5.1 | Struts2の“画面遷移の全体像”を理解するためのマイルストーン記事 | 設定ファイル構造 |
Vol.11.6 | ActionSupport の便利メソッドと国際化対応 | ユーティリティ / i18n |
Vol.11.7 | validate() / input 戦略とUX設計 | 開発スタイルの違い |
Vol.11.8 | アノテーションベースの設定 vs XML定義の比較 | 開発スタイルの違い |
Vol.11.9 | バリデーション完全解説(XML / アノテーション) | バリデーション(※Vol.8補完) |
Vol.11.10 | Struts2の「OGNL」ってなに?しくみと使い方をやさしく解説 | 式言語OGNL解説 |
Vol.11.11 | ModelDriven インターフェースの使いどころと注意点 | Model駆動開発 |
Vol.11.12 | SessionAware と RequestAware でセッション/リクエスト管理 | セッション管理 |
Vol.11.13 | Resultタイプ完全解説(dispatcher / redirect / stream など) | 遷移パターン理解 |
Vol.11.14 | Vol.11.14 ValueStackの中身を理解する – 画面とActionの橋渡し役 | Action |
Vol.11.15 | Interceptorチェーンを自作してみよう(ログ / 認証フィルタ) | Interceptor |
Vol.11.16 | エラー処理と例外ハンドリングの実践パターン | ハンドリング設計 |
Vol.11.17 | Struts2でファイルアップロード機能を実装する方法 | 実装系Tips |
Vol.11.18 | 非同期通信(AJAX)とStruts2の連携方法 | フロント連携編 |
Vol.11.19 | セキュリティ対策(CSRF, XSS, パラメータ偽装) | セキュリティ対策 |
Vol.11.21 | Struts2でJSONを返す!REST API連携と非同期通信の基本設計 | セッション管理 |
Vol.11.22 | 本番環境構成とセキュリティ設計(Apache+Tomcat+Struts2連携) | 外部公開 |