🧩 Vol.11.20 複数フォームを安全に扱う！セッション管理と動的フォームの考え方

>複数のフォームを同一画面・複数画面で扱う際に起こりやすい「競合」「衝突」「セッション不整合」などの問題を防ぐための設計と実装方針を、Struts2の視点から整理して解説します。

---

### ✅ 1. なぜ複数フォームはトラブルを招くのか？
**☑ 例：同一画面に複数フォームがある場合**
```jsp
<!-- 検索フォーム -->
<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度送信することを防ぐ`のに有効です。
```jsp
<s:form action="createUser">
  <s:token />
  <s:textfield name="username" label="ユーザ名" />
  <s:submit value="登録" />
</s:form>
```

```xml
<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>
```

***🔐 注意：***
複数のフォームで <s:token /> を使うと、同じセッション内で最後のトークンだけが有効になるため、
セッションに依存せず、JavaScriptで動的に送信先を切り替える方式も併用を検討します。

---

### ✅ 3. 対策②：フォーム単位での専用Action＋専用Formクラス設計

***❌ NGパターン：***

```java
public class UserAction extends ActionSupport {
    private String keyword; // 検索用
    private String username; // 登録用
    private String email;    // 登録用
    // フォームを兼ねたフィールドが混在
}

```


### ✅ OKパターン：
- SearchUserAction（検索専用）

- CreateUserAction（登録専用）

Actionクラスごとにフォームを完全に分離し、1アクション＝1フォームの原則を守ることで、
***フィールド競合・バリデーションの混乱を防止***できます。


---

### ✅ 4. 対策③：セッション一時保存はスコープと命名を厳密に管理

***☑ よくあるバグの元：***

```java
session.put("userInput", form);

```

→ 複数画面で同じキー名 `"userInput"` を使うと、***他フォームの入力が上書きされる***


### ✅ 改善例：

```java
session.put("createUserForm", form);
session.put("editUserForm", form);
```

または、***フォームの状態管理用にForm IDやタイムスタンプを付ける***方法もあります。


---


### ✅ 5. 対策④：動的フォーム生成時はhidden＋ID管理で状態を持つ

JavaScriptなどでフォームを動的に増やす場合：

```html
<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](https://qiita.com/juehara-crypto/items/a0cc66893cb499a6e23a)     | Struts2が提供してくれる便利機能まとめ9選                              | 導入＆概要             |
| [Vol.11.1](https://qiita.com/juehara-crypto/items/0a584da537059dd43a7c)     | 自動注入とステートレスActionの仕組み                                 | フレームワーク内部理解       |
| [Vol.11.2](https://qiita.com/juehara-crypto/items/c69962826bb29639c351) | **UX改善に効く！アクション層設計と入力制御の極意（プロローグ）**                   | UX設計・アクション層総論     |
| [Vol.11.2.1](https://qiita.com/juehara-crypto/items/9ca115073e6f928c7f71)   | 【実践編】Struts2アクション層の役割と設計パターン（Command型 / UIアクション分離など）  | アクション設計パターン       |
| [Vol.11.2.2](https://qiita.com/juehara-crypto/items/88b7634a64b1ad564668)   | 【UX重視】戻るボタン・キャンセル処理の設計手法と「戻り先管理」のスマート実装               | 戻り先制御とUX設計        |
| [Vol.11.2.3](https://qiita.com/juehara-crypto/items/c5165c92a4366fd1962b)   | 【設計ノウハウ】多画面遷移時のパラメータ受け渡し・保持戦略（セッション vs hidden vs URL） | パラメータ多重管理         |
| [Vol.11.2.4](https://qiita.com/juehara-crypto/items/06162659215cca86de28)   | 【実践ガイド】ユーザー操作を考慮したアクション遷移設計（リダイレクト vs フォワードの使い分け）     | UX遷移設計／Result制御   |
| [Vol.11.2.5](https://qiita.com/juehara-crypto/items/b538b50eaa9c263a31bb)   | 【トラブル防止】アクション層の共通処理とメンテナブルなBaseAction設計               | アクション共通化／保守性      |
| [Vol.11.3](https://qiita.com/juehara-crypto/items/913de7c942a9242201e8)     | Struts2の defaultStack とは？Interceptorの中身を詳しく追ってみる      | Interceptor       |
| [Vol.11.3.2](https://qiita.com/juehara-crypto/items/ede8d3c2a9b147f6161e)     | 🔧 Vol.11.3.2 独自Interceptorの作り方と使い所 _Struts2のカスタマイズを“実践で使えるレベル”に一歩進めよう_     | Interceptor       |
| [Vol.11.4](https://qiita.com/juehara-crypto/items/253142d13fe5f604610f)     | 入門者のための「パラメータ自動バインディング」完全理解ガイド                        | 自動バインディング         |
| [Vol.Vol.11.4.1](https://qiita.com/juehara-crypto/items/4fd8ed2a8be317f6e1bd)     | 入門者のための自動バインディング実践パターン集                        | 自動バインディング         |
| [Vol.11.5](https://qiita.com/juehara-crypto/items/6ef7a87a54163d81a9ba)     | Struts2の構成理解が“実務レベル”へと進化する決定版         | 設定ファイル構造          |
| [Vol.11.5.1](https://qiita.com/juehara-crypto/items/df407b24101e92a02eab)     | Struts2の“画面遷移の全体像”を理解するためのマイルストーン記事         | 設定ファイル構造          |
| [Vol.11.6](https://qiita.com/juehara-crypto/items/bfbdc2203acac7ba3f19)     | ActionSupport の便利メソッドと国際化対応                           | ユーティリティ / i18n    |
| [Vol.11.7](https://qiita.com/juehara-crypto/items/ed8f0977c23ee8c8b959)     | validate() / input 戦略とUX設計                            | 開発スタイルの違い    
| [Vol.11.8](https://qiita.com/juehara-crypto/items/bb2b23612c22fa9bf50b)     | アノテーションベースの設定 vs XML定義の比較                             | 開発スタイルの違い         |
| [Vol.11.9](https://qiita.com/juehara-crypto/items/6163ad6c9308cc8dda6d)     | バリデーション完全解説（XML / アノテーション）                            | バリデーション（※Vol.8補完） |
| [Vol.11.10](https://qiita.com/juehara-crypto/items/207cb3d7f0a889c54df9)     | Struts2の「OGNL」ってなに？しくみと使い方をやさしく解説                     | 式言語OGNL解説         |
| [Vol.11.11](https://qiita.com/juehara-crypto/items/c313ea56f97984756c05)    | ModelDriven インターフェースの使いどころと注意点                        | Model駆動開発         |
| [Vol.11.12](https://qiita.com/juehara-crypto/items/de7a17d53e53d6466f17)    | SessionAware と RequestAware でセッション/リクエスト管理            | セッション管理           |
| [Vol.11.13](https://qiita.com/juehara-crypto/items/539bd5bae75a8e154753)    | Resultタイプ完全解説（dispatcher / redirect / stream など）      | 遷移パターン理解          |
| [Vol.11.14](https://qiita.com/juehara-crypto/items/142d9aa2c623c5eb70bc)    | Vol.11.14 ValueStackの中身を理解する – 画面とActionの橋渡し役                 | Action            |
| [Vol.11.15](https://qiita.com/juehara-crypto/items/84f024816d0cf2212cb6)    | Interceptorチェーンを自作してみよう（ログ / 認証フィルタ）                 | Interceptor            |
| [Vol.11.16](https://qiita.com/juehara-crypto/items/ff3ad75f9ee45fcd0f8f)    | エラー処理と例外ハンドリングの実践パターン                                 | ハンドリング設計          |
| [Vol.11.17](https://qiita.com/juehara-crypto/items/0d8d5101ff39b34b4704)    | Struts2でファイルアップロード機能を実装する方法                           | 実装系Tips           |
| [Vol.11.18](https://qiita.com/juehara-crypto/items/d93c8d9cee719997c068)    | 非同期通信（AJAX）とStruts2の連携方法                              | フロント連携編           |
| [Vol.11.19](https://qiita.com/juehara-crypto/items/dd6cf06d3100c9204d1f)    | セキュリティ対策（CSRF, XSS, パラメータ偽装）                              | セキュリティ対策           |
| [Vol.11.21](https://qiita.com/juehara-crypto/items/78eb217727b7c97f228a)    | Struts2でJSONを返す！REST API連携と非同期通信の基本設計                              | セッション管理           |
| [Vol.11.22](https://qiita.com/juehara-crypto/items/424155ee0e567a36b9a1)    | 本番環境構成とセキュリティ設計（Apache+Tomcat+Struts2連携）                             | 外部公開           |

---