Visualforceページ間にパラメータを渡す方法整理

##Visualforceページ間に値を渡す方法について、本文にて整理します。

####◆URLパラメータを利用する
------------------
<B>　メリット：</B>
　　　・GETリクエストを使ってデータを渡す
　　　・実現するのに簡単
<B>　デメリット：</B>
　　　・URLの長さの制限がある『ブラウザにより異なるが、基本2000文字以内で良いでしょう』
　　　・次画面に渡す項目毎に設定、取得する必要がある
　　　・GETリクエス方式なので、セキュリティ面が強い場合不適切
　　　・連続な画面間に値を渡す必要がある場合、画面毎に取得、設定する必要がある
<B>　実装方法：</B>

```URLパラメータの実装例.cls
//【送信側】
testPage.getParameters().put('[パラメータ名]', '[値]');
//【受信側】
System.currentPageReference().getParameters().get('[パラメータ名]');
```

####◆Salesforceセッションキャッシュを利用する
------------------
<B>　メリット：</B>
　　　・Salesforceセッションキャッシュを利用するから、セキュリティー面が良い
　　　・一回セッションに設定したら、セッションキャッシュ内にどの画面も取得できる
<B>　デメリット：</B>
　　　・Salesforce内セッションキャッシュを利用するからこそ、Salesforceへログインする必要があり、コミュニティゲストユーザなどであれば、セッションキャッシュを利用できない
<B>　実装方法：</B>

```セッション格納の実装例.cls
// カスタム設定からセッションキャッシュの名前空間とパーティション名を取得する
// testSessionCache__Cカスタム設定にて設定されるオブジェクト名
testSessionCache__C tsc = testSessionCache__C.getOrgDefaults();
// カスタム設定からセッションキャッシュの有効期間を取得する
Integer ttlSecs = Integer.valueof(tfs.ValidTime__C);
// 名前空間にパーティションを定義する（データ重複を管理しやすいため）
String sessionPartition = tsc.NamespacePartition__C + '.test';
// セッションへ格納する必要なMapを作成
Map<String, Object> testMap = new Map<String, Object>();
testMap.put('key', 'value');
// セッション格納
Cache.Session.put(sessionPartition, testMap, ttlSecs);
```
```セッション取得の実装例.cls
// カスタム設定からセッションキャッシュの名前空間とパーティション名を取得する
testSessionCache__C tsc = testSessionCache__C.getOrgDefaults();
// 名前空間にセッション格納時に同じパーティションを定義する
String sessionPartition = tsc.NamespacePartition__C + '.test';
// セッションから項目を取得する
Map<String, Object> testMap = (Map<String, Object>)Cache.Session.get(sessionPartition);
```

####◆Salesforceビューステートを利用する
------------------
<B>　メリット：</B>
　　　・Salesforceビューステートを利用するから、セキュリティー面が良い
　　　・連続な画面間に値を渡すことも可能
<B>　デメリット：</B>
　　　・ビューステートのガバナ制限がある（170KB）
　　　・URLの表示欄に表示される画面IDが前画面のIDになる事象がある（詳細は後ほど説明する）
<B>　実装方法：</B>

```ビューステートの実装例.cls
//【前画面page】
<apex:page id="page1" controller="CommonCtrl" action="{!page1Init}">
    // ページ内容
</apex:page>
//【次画面】
<apex:page id="page1" controller="CommonCtrl" action="{!page2Init}">
    // ページ内容
</apex:page>
```

<B>URLの表示欄に表示される画面IDが前画面のIDになる事象について、詳細は以下の通りです。</B>
<B>ー事象展開</B>
例えば、次の遷移関係の画面がある：test01→test02→test03（三画はビューステートで値を渡す）
test01画面を表示時、URL欄に「xxsalesforcexxxxx/test01」で表示される。
test01画面に次へボタンを押下し、test02に遷移したら、URL欄に「xxsalesforcexxxxx/test01」のままです。
test02画面に次へボタンを押下し、test03に遷移したら、URL欄に「xxsalesforcexxxxx/test02」になる。
<B>ー解決案</B>
windows.historyを利用し、URL欄の表示値手動で設定しなおす。

<B>・URL直接遷移制御</B>
<B>ー事象展開</B>
業務上に複数の画面がある場合、画面間の遷移順が既に決まってるのはほとんどだと思われ、
URLに画面IDを直接入力し、前の画面を飛ばして直接特定の画面に直接遷移することを制御する必要となります。
<B>ー対応案</B>
　　・画面の次へボタンを押下時、Apex側に「ランダムな文字列」トークンを生成し、ビューステートに設定し、URLにトークンをパラメーターとして設定する
　　・次画面の初期表示イベントにApex側にURLからトークンを取得し、ビューステートのトークンと一致するかどうかを検証する。
　　・一致する場合、正常遷移とする。新しいトークンを生成して、ビューステートに更新する。
　　・一致しない場合、異常遷移として、エラー画面に遷移する。
<B>・補足</B>
この対応案で、URLの直接遷移制御ができるが、ブラウザの戻るボタンの制御ができない。
つまり、上記の実装を追加しても、ブラウザの戻るボタンを押下する場合、画面遷移できてしまうことです。
その原因はビューステートのPOSTBACK機能があるから、トークンの制御ができなくなる。
POSTBACKについて、詳細の説明は以下のリンクにある
　　　　[Visualforce ページの postback 要求の実行順序](https://developer.salesforce.com/docs/atlas.ja-jp.pages.meta/pages/pages_controller_postback_request.htm)
<B>・まとめ</B>
　　・以下のJSを利用し、上記の2つ事象を纏めて改善する方が良いでしょう。

```実装例.js
// ブラウザの履歴を削除する
windows.history.pushState(null, null, null);
// ブラウザに新しいURLを設定する
windows.addEventListener("DOMContentLoaded", function() {
    window.history.replaceState(null, null, '{!$Site.Prefix}/カレント画面ID');
});
// ブラウザの戻る／進むボタンも制御するなら、以下のコードで制御できる
windows.onpopstate = function() {
    location.href = '{!$Site.Prefix}/secur/logout.jsp';
}
```