CodeZine(コードジン)

特集ページ一覧

ASP.NET Webフォームアプリケーションでの状態管理の使い分け

実例で学ぶASP.NET Webフォーム業務アプリケーション開発のポイント 第4回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2012/06/28 14:00
目次

ViewState、Sessionを用いた多重送信防止

 ViewState、Sessionの両方を活用した例として、今回は「ワンタイムトークン」を用いた多重送信防止方法を紹介します。

多重送信とは

 WebアプリケーションはHTTPリクエストに応じて処理を行う仕組みであるため、同じHTTPリクエストを複数回送信することで、同じ処理を何度も実行することが原理的に可能になっています。これは、ASP.NET Webフォームに限った話ではなく、例えばボタンの2度押し、F5キーによるページ更新などにより、どんなWebアプリケーションでも発生します。

 多重送信が行われるとデータの更新処理が複数回行われ、データが不正な状態になってしまうなどの問題が生じます。

 このことを防ぐには、すでに実行済みかどうかの「状態」を管理し、実行済みであれば処理をキャンセルするといった方法を取る必要があります。

ワンタイムトークンを用いた多重送信防止

 多準送信を防ぐ方法の一つが「ワンタイムトークン」を用いた方法です。その手順について、概念を説明します。

[1]ページ初回表示時

 新たに「トークン」を発行し、クライアントサイドとサーバーサイドに持たせます。

図3:ページ初回表示時
図3:ページ初回表示時

 このトークンは一意であればどのような値でも構いません。

[2]処理実行

 処理を実行する際、クライアントから[1]で発行されたトークンもサーバーに送ります。

図4:処理実行時
図4:処理実行時

 サーバーではクライアントから送られたトークンとサーバーで保持しているトークンを比較します。

[3]トークンの判定

 クライアントから送られたトークンがサーバーで保持しているものが同じなら、新たにトークンを発行しサーバーサイドのトークンを置き換えます。

図5:新たなトークン発行
図5:新たなトークン発行

 その後、本来行うべき業務処理を行い、処理終了後に先ほど発行した新たなトークンをクライアントサイドに返します。

 多重送信が行われた際は、クライアントから送られたトークンとサーバーで保持しているトークンが一致しません。

図6:多重送信発生時
図6:多重送信発生時

 このときは、業務処理の実行をキャンセルし、トークンの発行もせず処理を終了します。

ViewStateとSessionを使ったワンタイムトークン実装

 ASP.NET Webフォームアプリケーションでは、クライアントサイドはViewState、サーバーサイドはSessionにトークンを保持するようにします。

 サンプルアプリケーションの場所の追加処理に組み込んだ実装例を、以下に示します。

[1]トークンの発行

 PageのLoadイベントの初回実行時、トークンを発行しViewStateとSessionに保存します。

リスト2 LocationsAdd.aspx.cs Page.Loadイベントハンドラー
protected void Page_Load(object sender, EventArgs e)
{
  if (!Page.IsPostBack)
  {
    // トークンの発行
    string token = GenerateToken(); // (1)
    ViewState["LocationsAdd_Token"] = token;  // (2)
    Session["LocationsAdd_Token"] = token;  // (3)
  }
}

// トークンの生成
protected string GenerateToken()
{
  return Guid.NewGuid().ToString(); // (1)
}

 (1)発行するトークンは「一意である」ことが重要です。何らかの乱数でもよいのですが、今回は一意であることがほぼ保障されているGUID(Globally Unique Identifier、グローバル一意識別子)を用います。

 (2)クライアントサイドへのトークン保存をViewStateで行います。

 (3)サーバーサイドへのトークン保存をSessionで行います。

[2]トークンの判定

 場所情報を扱うFormViewコントロールのItemInsertingイベントで、追加処理を行う前にトークンの判定を行います。なお、トークンをViewStateに保存することで、処理実行時にサーバーにトークンを送る処理は自動化されます。

リスト3 LocationsAdd.aspx.cs LocationsFormView.ItemInsertingイベントハンドラー
protected void LocationsFormView_ItemInserting(object sender,
  FormViewInsertEventArgs e)
{
  // 保存してあるトークンの取得
  var vsToken = ViewState["LocationsAdd_Token"] as string; // (1)
  var sToken = Session["LocationsAdd_Token"] as string;    // (2)

  // トークンが一致するか判定
  if (vsToken == sToken)
  {
    // 一致
    // 新たなトークンの発行
    var newToken = GenerateToken(); // (3)
    Session["LocationsAdd_Token"] = newToken;
    ViewState["LocationsAdd_Token"] = newToken;
  }
  else
  {
    // 不一致
    // 追加処理のキャンセル
    e.Cancel = true;  // (4)

    // エラー表示
    PageUtilities.ShowErrorMessage(this,
      "追加処理は実行済みのため、今回の処理はキャンセルしました。");  // (5)
  }
}

 (1)クライアントサイドから送られたトークンをViewStateから取得します。

 (2)サーバーサイドに保持しているトークンをSessionから取得します。

 (3)クライアントサイドとサーバーサイドのトークンが一致していた場合、新たにトークンを発行し、それぞれSessionとViewStateに格納します。

 (4)クライアントサイドとサーバーサイドのトークンが一致しなかった場合、すでに追加処理が実行されたことになります。そのため、今回の追加処理をキャンセルしなければなりませんので、FormViewInsertingEventArgsオブジェクトのCancelプロパティにtrueを設定します。

 (5)今回の追加処理がキャンセルされたことを表すエラーメッセージを表示します。PageUtilities.ShowErrorMessageメソッドについては、前回の記事を参照してください。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:実例で学ぶASP.NET Webフォーム業務アプリケーション開発のポイント

もっと読む

著者プロフィール

  • WINGSプロジェクト 高野 将(タカノ ショウ)

    <個人紹介> 新潟県長岡市在住の在宅リモートワークプログラマー。家事や育児、仕事の合間に長岡IT開発者勉強会(NDS)、Niigata.NET、TDDBCなどのコミュニティに関わったり、Web記事や書籍などの執筆を行ったりしている。著書に『アプリを作ろう! Visual C#入門 Visual C...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XM...

あなたにオススメ

All contents copyright © 2005-2022 Shoeisha Co., Ltd. All rights reserved. ver.1.5