PRGパターンとHistory APIを使った画面遷移について

# 概要

登録処理でPRGパターンを使う場合と使わない場合の画面遷移の挙動を検証したときの記録です。
検証で使用したWebアプリケーションはJavaのSpring Bootで開発したものです。

**検証パターン**

1. PRGパターンを使う場合
2. PRGパターンを使わない場合
3. PRGパターンを使わない + History APIを使う場合

**環境**

* Windows10 Professional
* Java 1.8.0_101
* Spring Boot 1.4.1
* Chrome 54
* jQuery 1.12

**参考**

* [MDN - Web technology For developers - Web APIs - History] (https://developer.mozilla.org/en-US/docs/Web/API/History)
* [MDN - ブラウザの履歴を操作する] (https://developer.mozilla.org/ja/docs/Web/Guide/DOM/Manipulating_the_browser_history)
* [Post/Redirect/Get (PRG) パターン] (http://qiita.com/furico/items/a32c106e9d7c4418fc9d)


## 1. PRGパターンを使う場合

下図の「RPGパターンを使う」ときの画面遷移の挙動を確認します。

**画面補足**

* `[A] /index` TOPページ
* `[B] /entry` 登録フォームページ
* `[C] /regist` 登録処理 (画面なし)、処理後に[D]へリダイレクト
* `[D] /result` 登録結果ページ
* `[E] /other` 他のページ

**確認のポイント**

1. `[D] /result`から戻るボタンを押したときの遷移先について
1. `[D] /result`でページ再読み込みを行った後に戻るボタンを押したときの遷移先について
1. `[E] /other`から戻るボタンを押したときの遷移先について

```text:PRGパターンを使う場合の遷移図

                  .....................................................................
                  .                                                                   .
  [A]             .   [B]                    [C]                        [D]           .     [E]
  /index          .   /entry                 /regist                    /result       .     /other
 +-----------+    .  +-------------+        +--------------+ REDIRECT  +-----------+  .    +-----------+
 | Index     |  GET  | Entry Form  |  POST  | Registration |  / GET    | Result    |  GET  | Other     |
 | (Page)    |------>| (Page)      |------->|              |---------->| (Page)    |------>| (Page)    |
 |           |    .  |             |        |              |           |           |  .    |           |
 +-----------+    .  +-------------+        +--------------+           +-----------+  .    +-----------+
                  .             ^                                         |    ^      .             |
                  .             |                                         |    |      .             |
                  .             +--------------[Browser back]<------------+    +---[Browser back]<--+
                  .                                                                   .
                  .....................................................................
                   PRG pattern


AddressバーのURL
  [A]-----------------[B]--------------------[C]------------------------[D]-----------------[E]
  /index              /entry                                            /result             /other

スタックされたHistory
  [A]---------------->[B]------------------->[C]----------------------->[D]---------------->[E]
                      /index                                            /entry              /result
                                                                        /index              /entry
                                                                                            /index
```

### 動作確認の結果

* `[D] /result`へ遷移すると、historyに`[B] /entry`と、`[A] /index`が記録されている。
* `[D] /result`からブラウザの戻るボタンで戻ると`[B] /entry`へ遷移する。 (`[B] /entry`へのGETリクエストは発生しない)
* `[D] /result`でページ再読み込みをしてもhistoryは変わらない。 (`[D] /result`へのGETリクエストが発生する)
* `[E] /other`からブラウザの戻るボタンで戻ると`[D] /result`へ遷移する。 (`[D] /result`へのGETリクエストは発生しない)

#### この画面遷移の問題点

* `[D] /result`からブラウザの戻るボタンで`[B] /entry`へ戻り、再度フォームをサブミットすることができてしまう。
 * 対策として登録処理で2重登録のチェック処理の実装、登録時にワンタイムトークンを使用する。(PRGパターンの問題ではありません)
* `[C] /regist`から`[D] /result`へリダイレクトする際にflash scopeを使ってメッセージを渡す場合、`[D] /result`でページ再読み込みをするとそのメッセージが失われる。

### 動作確認の結果 (Chromeのdevtoolsでキャッシュを無効化した状態)

赤字のところは上記のキャッシュが有効化されている場合との相違点です。

* `[D] /result`へ遷移すると、historyに`[B] /entry`と、`[A] /index`が記録されている。
* `[D] /result`からブラウザの戻るボタンで戻ると[B] /entryへ遷移する。 (<font color="red">`[B] /entry`へのGETリクエストが発生する</font>)
* `[D] /result`でページ再読み込みをしてもhistoryは変わらない。 (`[D] /result`へのGETリクエストが発生する)
* `[E] /other`からブラウザの戻るボタンで戻ると`[D] /result`へ遷移する。 (<font color="red">`[D] /result`へのGETリクエストが発生する</font>)

#### この画面遷移の問題点

* `[D] /result`からブラウザの戻るボタンで`[B] /entry`へ戻り、再度フォームをサブミットすることができてしまう。
 * 対策として登録処理で2重登録のチェック処理の実装する。(PRGパターンの問題ではありません)
 * `[B] /entry`が再読み込みされるのでワンタイムトークンの実装は有効ではない。
* `[C] /regist`から`[D] /result`へリダイレクトする際にflash scopeを使ってメッセージを渡す場合、`[D] /result`でページ再読み込みをするとそのメッセージが失われる。


### 検証用のコード

#### Form

```java:RedirectForm
public class RedirectForm implements Serializable {
  private static final long serialVersionUID = -2487350655211970792L;
  private String name;
  private String email;

  ...getter/setter省略...

  @Override
  public String toString() {
    return "RedirectForm [name=" + name + ", email=" + email + "]";
  }
}
```

#### Controller

```java:RedirectController
@Controller
@RequestMapping(value = "/redirect")
public class RedirectController {

  @RequestMapping(value = "/entry", method = RequestMethod.GET)
  public String entry(Model model) {
    Date current = new Date();
    model.addAttribute("current", current);
    return "redirect/entry";
  }

  @RequestMapping(value = "/regist", method = RequestMethod.POST)
  public String regist(RedirectForm form, RedirectAttributes attributes) {

    // ここはformの内容をDBに登録する処理
    // 登録IDを取得（みなし）
    String id = UUID.randomUUID().toString();
    // 登録メッセージ
    attributes.addFlashAttribute("message", "登録完了");

    return "redirect:/redirect/result/" + id;
  }

  @RequestMapping(value = "/result/{id}", method = RequestMethod.GET)
  public String result(@PathVariable("id") String id, Model model) {

    // ここはIDから登録内容を検索する処理
    // 検索結果をmodelにセット
    model.addAttribute("name", "rubytomato");
    model.addAttribute("email", "rubytomato@example.com");

    Date current = new Date();
    model.addAttribute("current", current);
    return "redirect/result";
  }

  @RequestMapping(value = "/other", method = RequestMethod.GET)
  public String other(Model model) {
    Date current = new Date();
    model.addAttribute("current", current);
    return "redirect/other";
  }

}
```

#### Template

**index.html**

```html
<body>

  <div class="container-fluid">
    <div class="row">
      <div class="col-md-12 well">
        <ul>
          <li><a href="/redirect/entry">redirect [A] entry</a></li>
          <li><a href="/redirect2/entry">redirect2 [A] entry</a></li>
          <li><a href="/redirect3/entry">redirect3 [A] entry</a></li>
        </ul>
      </div>
    </div>
  </div>

</body>
```

**entry.html**

```html
<body>

  <div class="container-fluid">
    <div class="row">
      <div class="col-md-12 well">
        <h2>[B] entry</h2>
        <form action="/redirect/regist" method="POST" class="form-horizontal">
          <div class="form-group">
            <label for="name" class="col-sm-2 control-label">name</label>
            <div class="col-sm-10">
              <input class="form-control" id="name" name="name" type="text" />
            </div>
          </div>
          <div class="form-group">
            <label for="email" class="col-sm-2 control-label">email</label>
            <div class="col-sm-10">
              <input class="form-control" id="email" name="email" type="email" />
            </div>
          </div>
          <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
              <button type="submit" class="btn btn-default">送信</button>
            </div>
          </div>
        </form>
      </div>
      <div class="col-md-12 well">
        <div>
          <p>現在日時:<span th:text="${current}">current</span></p>
        </div>
        <div>
          <button class="back btn btn-default">戻る</button>
          <button class="forward btn btn-default">進む</button>
        </div>
      </div>
    </div>
  </div>

</body>
```

**result.html**

```html
<body>

  <div class="container-fluid">
    <div class="row">
      <div class="col-md-12 well">
        <h2>[D] result</h2>
        <dl>
          <dt>id</dt>
          <dd th:text="${id}">id</dd>
          <dt>name</dt>
          <dd th:text="${name}">name</dd>
          <dt>email</dt>
          <dd th:text="${email}">email</dd>
        </dl>
        <div>
          <a href="/redirect/other">他のページ</a>
        </div>
      </div>
      <div class="col-md-12 well">
        <div>
          <p>flash scope message</p>
          <p th:text="${message}">message</p>
        </div>
        <div>
          <p>現在日時:<span th:text="${current}">current</span></p>
        </div>
        <div>
          <button class="back btn btn-default">戻る</button>
          <button class="forward btn btn-default">進む</button>
        </div>
      </div>
    </div>
  </div>

</body>
```

**other.html**

```html
<body>

  <div class="container-fluid">
    <div class="row">
      <div class="col-md-12 well">
        <h2>[E] other</h2>
      </div>
      <div class="col-md-12 well">
        <div>
          <p>現在日時:<span th:text="${current}">current</span></p>
        </div>
        <div>
          <button class="back btn btn-default">戻る</button>
          <button class="forward btn btn-default">進む</button>
        </div>
      </div>
    </div>
  </div>

</body>
```



## 2. PRGパターンを使わない場合

下図の「RPGパターンを使わない」ときの画面遷移の挙動を確認します。

**画面補足**

* `[A] /index` TOPページ
* `[B] /entry` 登録フォームページ
* `[C] /regist` 登録処理とその結果ページ
* `[E] /other` 他のページ

**確認のポイント**

1. `[C] /regist`から戻るボタンを押したときの遷移先について
1. `[C] /regist`でページ再読み込みを行った後に戻るボタンを押したときの遷移先について
1. `[E] /other`から戻るボタンを押したときの遷移先について

```text:PRGパターンを使わない場合の遷移図

  [A]                 [B]                    [C]                                            [E]
  /index              /entry                 /regist                                        /other
 +-----------+       +-------------+        +--------------+                               +-----------+
 | Index     |  GET  | Entry Form  |  POST  | Registration |                          GET  | Other     |
 | (Page)    |------>| (Page)      |------->|  & Result    |------------------------------>| (Page)    |
 |           |       |             |        | (Page)       |                               |           |
 +-----------+       +-------------+        +--------------+                               +-----------+
                          ^                       |     ^                                       |
                          |                       |     |                                       |
                          +----[Browser back]<----+     +------------[Browser back]<------------+


AddressバーのURL
  [A]-----------------[B]--------------------[C]---------------------------------------------[E]
  /index              /entry                 /regist                                         /other

スタックされたHistory
  [A]---------------->[B]------------------->[C]-------------------------------------------->[E]
                      /index                 /entry                                          /regist
                                             /index                                          /entry
                                                                                             /index
```

### 動作確認の結果

* `[C] /regist`へ遷移すると、historyに`[B] /entry`と、`[A] /index`が記録されている。
* `[C] /regist`からブラウザの戻るボタンで戻ると`[B] /entry`へ遷移する。(`[B] /entry`へのGETリクエストは発生しない)
* `[C] /regist`でページ再読み込みをしてもhistoryは変わらない。 (`[C] /regist`へのPOSTリクエストが発生する)
* `[E] /other`からブラウザの戻るボタンで戻ると`[C] /regist`へ遷移する。 (`[C] /regist`へのPOSTリクエストは発生しない)

### この画面遷移の問題点

* `[C] /regist`からブラウザの戻るボタンで`[B] /entry`へ戻り、再度フォームをサブミットすることができてしまう。
 * 対策として登録処理で2重登録のチェック処理の実装、登録時にワンタイムトークンを使用する。(PRGパターンの問題ではありません)
* `[C] /regist`でページ再読み込みを行うと登録処理が再び実行されてしまう。
 * 対策としてRPGパターンを適用する。

### 動作確認の結果 (Chromeのdevtoolsでキャッシュを無効化した状態)

赤字のところは上記のキャッシュが有効化されている場合との相違点です。

* `[C] /regist`へ遷移すると、historyに`[B] /entry`と、`[A] /index`が記録されている。
* `[C] /regist`からブラウザの戻るボタンで戻ると[B] /entryへ遷移する。(<font color="red">`[B] /entry`へのGETリクエストが発生する</font>)
* `[C] /regist`でページ再読み込みをしてもhistoryは変わらない。 (`[C] /regist`へのPOSTリクエストが発生する)
* `[E] /other`からブラウザの戻るボタンで戻ると`[C] /regist`へ遷移する。 (<font color="red">`[C] /regist`へのPOSTリクエストが発生する</font>)

#### この画面遷移の問題点

* `[C] /regist`からブラウザの戻るボタンで`[B] /entry`へ戻り、再度フォームをサブミットすることができてしまう。
 * 対策として登録処理で2重登録のチェック処理の実装する。(PRGパターンの問題ではありません)
 * `[B] /entry`が再読み込みされるのでワンタイムトークンの実装は有効ではない。
* `[C] /regist`でページ再読み込みを行う、または`[E] /other`からブラウザの戻るボタンで戻ってくると登録処理が再び実行されてしまう。
 * 対策としてRPGパターンを適用する。


### 検証用のコード

#### Form

PRGパターンを使う場合と同じ

#### Controller

```java:Redirect2Controller
@Controller
@RequestMapping(value = "/redirect2")
public class Redirect2Controller {

  @RequestMapping(value = "/entry", method = RequestMethod.GET)
  public String entry(Model model) {
    Date current = new Date();
    model.addAttribute("current", current);
    return "redirect2/entry";
  }

  @RequestMapping(value = "/regist", method = RequestMethod.POST)
  public String regist(RedirectForm form, Model model) {

    // ここはformの内容をDBに登録する処理
    // 登録IDを取得（みなし）
    String id = UUID.randomUUID().toString();
    model.addAttribute("id", id);
    // 登録結果をmodelにセット
    model.addAttribute("name", "rubytomato");
    model.addAttribute("email", "rubytomato@example.com");
    // 登録メッセージ
    model.addAttribute("message", "登録完了");

    Date current = new Date();
    model.addAttribute("current", current);

    return "redirect2/regist";
  }

  @RequestMapping(value = "/other", method = RequestMethod.GET)
  public String other(Model model) {
    Date current = new Date();
    model.addAttribute("current", current);
    return "redirect2/other";
  }

}
```

#### Template

**index.html**

PRGパターンを使う場合と同じ

**entry.html**

```html
<body>

  <div class="container-fluid">
    <div class="row">
      <div class="col-md-12 well">
        <h2>[B] entry</h2>
        <form action="/redirect2/regist" method="POST" class="form-horizontal">
          <div class="form-group">
            <label for="name" class="col-sm-2 control-label">name</label>
            <div class="col-sm-10">
              <input class="form-control" id="name" name="name" type="text" />
            </div>
          </div>
          <div class="form-group">
            <label for="email" class="col-sm-2 control-label">email</label>
            <div class="col-sm-10">
              <input class="form-control" id="email" name="email" type="email" />
            </div>
          </div>
          <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
              <button type="submit" class="btn btn-default">送信</button>
            </div>
          </div>
        </form>
      </div>
      <div class="col-md-12 well">
        <div>
          <p>現在日時:<span th:text="${current}">current</span></p>
        </div>
        <div>
          <button class="back btn btn-default">戻る</button>
          <button class="forward btn btn-default">進む</button>
        </div>
      </div>
    </div>
  </div>

</body>
```

**regist.html**

```html
<body>

  <div class="container-fluid">
    <div class="row">
      <div class="col-md-12 well">
        <h2>[C] regist</h2>
        <dl>
          <dt>id</dt>
          <dd th:text="${id}">id</dd>
          <dt>name</dt>
          <dd th:text="${name}">name</dd>
          <dt>email</dt>
          <dd th:text="${email}">email</dd>
        </dl>
        <div>
          <a href="/redirect2/other">他のページ</a>
        </div>
      </div>
      <div class="col-md-12 well">
        <div>
          <p>message</p>
          <p th:text="${message}">message</p>
        </div>
        <div>
          <p>現在日時:<span th:text="${current}">current</span></p>
        </div>
        <div>
          <button class="back btn btn-default">戻る</button>
          <button class="forward btn btn-default">進む</button>
        </div>
      </div>
    </div>
  </div>

</body>
```

**other.html**

PRGパターンを使う場合と同じ


## 3. PRGパターンを使わない + History APIを使う場合

下図の「RPGパターンを使わない + History APIを使う」ときの画面遷移の挙動を確認します。
`[C] /regist`へ遷移したときにHistory APIのreplaceStateを使って履歴を`[C] /regist`から`[D] /result`へ書き換えます。
これによってブラウザのアドレスバーは/resultとなります。
`[D] /result`のページは、`[C] /regist`(アドレスバー上は/result)でページ再読み込みを行ったときや、`[E] /other`から戻ってくるときのために用意します。

**画面補足**

* `[A] /index` TOPページ
* `[B] /entry` 登録フォームページ
* `[C] /regist` 登録処理とその結果ページ
* `[D] /result` 登録結果ページ
* `[E] /other` 他のページ

`[C] /regist`と`[D] /result`は同じhtmlテンプレートを使います。

**確認のポイント**

1. `[C] /regist`から戻るボタンを押したときの遷移先について
1. `[C] /regist`でページ再読み込みを行った後に戻るボタンを押したときの遷移先について
1. `[E] /other`から戻るボタンを押したときの遷移先について

```text:PRGパターンを使わない+HistoryAPIを使う場合の遷移図

  [A]                 [B]                    [C]                                            [E]
  /index              /entry                 /regist (->/result)                            /other
 +-----------+       +-------------+        +--------------+                               +-----------+
 | Index     |  GET  | Entry Form  |  POST  | Registration |                          GET  | Other     |
 | (Page)    |------>| (Page)      |------->|  & Result    |------------------------------>| (Page)    |
 |           |       |             |        | (Page)       |                          +--->|           |
 +-----------+       +-------------+        +--------------+                          |    +-----------+
                          ^                       |     |                             |             |
                          |                       |     |               [D]           |             |
                          |<---[Browser back]<----+     +-----[F5]----> /result       |             |
                          |                                            +-----------+  |             |
                          |                                            | Result    |--+             |
                          |                                            | (Page)    |                |
                          |                                            |           |                |
                          |                                            +-----------+                |
                          |                                               |    ^                    |
                          |                                               |    |                    |
                          +--------------------[Browser back]<------------+    +---[Browser back]<--+

AddressバーのURL
  [A]-----------------[B]--------------------[C]---------------------------------------------[E]
  /index              /entry                 /regist                                         /other
                                                ↓ (History APIで書き換え)
                                             /result
                                                
スタックされたHistory
  [A]---------------->[B]------------------->[C]-------------------------------------------->[E]
                      /index                 /entry                                          /result <---この履歴はHistory APIで書き換えたもの
                                             /index                                          /entry
                                                                                             /index
                                                                        [D]<-----------------[E]
                                                                        /entry
                                                                        /index
```

### 動作確認の結果

* `[C] /regist`へ遷移すると、historyに`[B] /entry`と、`[A] /index`が記録されている。
* `[C] /regist`(アドレスバー上は/result)からブラウザの戻るボタンで戻ると`[B] /entry`へ遷移する。(`[B] /entry`へのGETリクエストは発生しない)
* `[C] /regist`(アドレスバー上は/result)でページ再読み込みをしてもhistoryは変わらない。 (`[D] /result`へのGETリクエストが発生する)
* `[C] /regist`を再読み込みした後の`[D] /result`からブラウザの戻るボタンで戻ると`[B] /entry`へ遷移する。(`[B] /entry`へのGETリクエストは発生しない)
* `[E] /other`からブラウザの戻るボタンで戻ると`[D] /result`へ遷移する。 (`[D] /result`へのGETリクエストは発生しない)

#### この画面遷移の問題点

* `[C] /regist`または`[D] /result`からブラウザの戻るボタンで`[B] /entry`へ戻り、再度フォームをサブミットすることができてしまう。
 * 対策として登録処理で2重登録のチェック処理の実装、登録時にワンタイムトークンを使用する。(PRGパターンの問題ではありません)

### 動作確認の結果 (Chromeのdevtoolsでキャッシュを無効化した状態)

* `[C] /regist`へ遷移すると、historyに`[B] /entry`と、`[A] /index`が記録されている。
* `[C] /regist`(アドレスバー上は/result)からブラウザの戻るボタンで戻ると`[B] /entry`へ遷移する。(<font color="red">`[B] /entry`へのGETリクエストが発生する</font>)
* `[C] /regist`(アドレスバー上は/result)でページ再読み込みをしてもhistoryは変わらない。 (`[D] /result`へのGETリクエストが発生する)
* `[C] /regist`を再読み込みした後の`[D] /result`からブラウザの戻るボタンで戻ると`[B] /entry`へ遷移する。(`[B] /entry`へのGETリクエストが発生する)
* `[E] /other`からブラウザの戻るボタンで戻ると`[D] /result`へ遷移する。 (<font color="red">`[D] /result`へのGETリクエストが発生する</font>)

#### この画面遷移の問題点

* `[D] /result`からブラウザの戻るボタンで`[B] /entry`へ戻り、再度フォームをサブミットすることができてしまう。
 * 対策として登録処理で2重登録のチェック処理の実装する。(PRGパターンの問題ではありません)
 * `[B] /entry`が再読み込みされるのでワンタイムトークンの実装は有効ではない。



### 検証用のコード

#### Form

PRGパターンを使う場合と同じ

#### Controller

```java:Redirect3Controller
@Controller
@RequestMapping(value = "/redirect3")
public class Redirect3Controller {

  @RequestMapping(value = "/entry", method = RequestMethod.GET)
  public String entry(Model model) {
    Date current = new Date();
    model.addAttribute("current", current);
    return "redirect3/entry";
  }

  @RequestMapping(value = "/regist", method = RequestMethod.POST)
  public String regist(RedirectForm form, Model model) {

    // ここにformの内容をDBに登録する処理
    // 登録IDを取得（みなし）
    String id = UUID.randomUUID().toString();
    model.addAttribute("id", id);
    // 登録結果をmodelにセット
    model.addAttribute("name", "rubytomato");
    model.addAttribute("email", "rubytomato@example.com");
    // 登録メッセージ
    model.addAttribute("message", "登録完了");

    Date current = new Date();
    model.addAttribute("current", current);
    return "redirect3/regist";
  }

  @RequestMapping(value = "/result/{id}", method = RequestMethod.GET)
  public String result(@PathVariable("id") String id, Model model) {
    model.addAttribute("id", id);

    // ここにIDから登録情報を検索する処理
    // 検索結果をmodelにセット
    model.addAttribute("name", "rubytomato");
    model.addAttribute("email", "rubytomato@example.com");
    
    Date current = new Date();
    model.addAttribute("current", current);
    return "redirect3/regist";
  }

  @RequestMapping(value = "/other", method = RequestMethod.GET)
  public String other(Model model) {
    Date current = new Date();
    model.addAttribute("current", current);
    return "redirect3/other";
  }

}
```

#### Template

**index.html**

PRGパターンを使う場合と同じ

**entry.html**

```html
<body>

  <div class="container-fluid">
    <div class="row">
      <div class="col-md-12 well">
        <h2>[B] entry</h2>
        <form action="/redirect3/regist" method="POST" class="form-horizontal">
          <div class="form-group">
            <label for="name" class="col-sm-2 control-label">name</label>
            <div class="col-sm-10">
              <input class="form-control" id="name" name="name" type="text" />
            </div>
          </div>
          <div class="form-group">
            <label for="email" class="col-sm-2 control-label">email</label>
            <div class="col-sm-10">
              <input class="form-control" id="email" name="email" type="email" />
            </div>
          </div>
          <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
              <button type="submit" class="btn btn-default">送信</button>
            </div>
          </div>
        </form>
      </div>
      <div class="col-md-12 well">
        <div>
          <p>現在日時:<span th:text="${current}">current</span></p>
        </div>
        <div>
          <button class="back btn btn-default">戻る</button>
          <button class="forward btn btn-default">進む</button>
        </div>
      </div>
    </div>
  </div>

</body>
```

**regist.html**

```html
<body>

  <div class="container-fluid">
    <div class="row">
      <div class="col-md-12 well">
        <h2>[C] regist</h2>
        <dl>
          <dt>id</dt>
          <dd th:text="${id}">id</dd>
          <dt>name</dt>
          <dd th:text="${name}">name</dd>
          <dt>email</dt>
          <dd th:text="${email}">email</dd>
        </dl>
        <div>
          <a href="/redirect3/other">他のページ</a>
        </div>
      </div>
      <div class="col-md-12 well">
        <div>
          <p>message</p>
          <p th:text="${message}">message</p>
        </div>
        <div>
          <p>現在日時:<span th:text="${current}">current</span></p>
        </div>
        <div>
          <button class="back btn btn-default">戻る</button>
          <button class="forward btn btn-default">進む</button>
        </div>
      </div>
    </div>
  </div>

<script type="text/javascript" th:inline="javascript">
/*<![CDATA[*/
$(function(){
    if (window.location.pathname.indexOf("regist") != -1) {
        console.log("regist");
        var state = {a:1, b:2};
        window.history.replaceState(state, "result", /*[[ '/redirect3/result/' + ${id} ]]*/ "/redirect3/result/123456");
    }
});
/*]]>*/
</script>
</body>
```

**other.html**

PRGパターンを使う場合と同じ



## History API

### Methods

#### pushState

履歴をスタックするメソッド。

```js:例
//履歴の追加
history.pushState(null, null, "/result");
```

#### replaceState

履歴の修正を行うメソッド。
通常はpushStateメソッドでスタックした履歴を修正するときに使用する。

```js:例
//履歴の修正
history.replaceState(null, null, "/result");
```

### Properties

#### length

スタックされているページ数を知る。

```js:例
history.length;
```

#### state

現在の履歴エントリのstateを読み取る。

```js:例
history.state;
```

### イベント

#### popstate

pushStateメソッドでスタックした履歴へブラウザの戻る・進むボタンをクリックして移動したときに発生するイベント。
History APIを使わずに通常の画面遷移でスタックされた履歴ではイベントは発生しない。

```js:例
$(window).on('popstate', function(event){
  //ブラウザの戻る・進むボタンがクリックされたときに行いたい処理を実装
});
```