書籍転載
独習ASP.NET 第3版

ASP.NETを理解する3つの仕組み
― 第2章 ASP.NET の基礎 2.3 ―

WINGSプロジェクト 山田 祥寛
2011/07/06
Page1 Page2 Page3

2.3.3 ビューステート

 繰り返しますが、ASP.NETではリクエストのたびにPageオブジェクトは破棄されるのが基本です。つまり、ページを初回表示したときに利用されるPageオブジェクトと、ポストバック時に利用されるPageオブジェクトとは別ものであるということです(図2.38)。

図2.38 Pageオブジェクトはそのつど生成される

 これによって、どのような問題が発生するでしょうか。

 たとえば、テキストボックスの変更イベントを検知したい場合にも、ポストバックによって送信される値はあくまで変更後の値のみです。これは困ったことで、このままではサーバーサイドで値が変更されたという事実を捕捉できません。

 そこで登場するのが、ビューステート(ViewState)という仕組みです。ビューステートとは、現在のページ(View)の状態(State)を保持しておくための仕組みです。

 もっとも、なんら特別な仕組みではありません。試しに、もう一度、Hello.aspxを実行し、ブラウザーから[ソースの表示]を選択してください。ソース中に、以下のような記述が見つかるはずです。

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTU5RMTA2ODYwOQ9kFgICAw9kFgICBQ8PFgIeBFRleHQFJ+OBk+OCk+OBq+OBoeOBr+OAgeWxseeUsOelRpeWvm+OBleOCk++8gWRkZA+gfGkyIlcPonhPs2PkYZVauEm6PhU09ChDDUbZD5I6" />

 この隠しフィールドがビューステートの正体です。ビューステートはBase64エンコードされていますので、一見すると、意味のない文字列の羅列に見えますが、これをデコードするのは簡単です。サーバーサイドでは送信されたビューステートで、ページの以前の状態を復元し、変更系の処理などを行うことになります。

 ビューステートは、イベントドリブン/ポストバックの仕組みを支えるASP.NETの基本的な仕組みなのです。最初のうちはそれほど意識する機会はないかもしれませんが、こうした原始的な仕組みを知っておくことで、ASP.NET固有の挙動に戸惑うことも少なくなるはずです。きちんとおさえておきましょう。

* Base64とは、データを 64種類の文字で表現するエンコード方法です。シングルバイト文字しか利用できない環境で、バイナリデータやマルチバイト文字を扱うために利用されます。デコードするのもごく簡単で、たとえばV iewStateDecoder( http://www.pluralsight.com/community/media/p/51688.aspx)のようなツールを利用することで、ビューステートのもともとの値を確認できます(自分でデコードするのもさほど難しいことではありません)。ViewStateDecoderのメイン画面の左上にある[Enter the URL.]欄にビューステートを取得したいURLを入力して、[Extract Viewstat]ボタンをクリックするか、[ViewState string]欄にビューステートの値を直接コピー&ペーストしたうえで、[ Decode->]ボタンをクリックします。以下のようにオリジナルのビューステートが表示されることを確認してください。

ViewStateDecoderのメイン画面

ビューステートが生成されるもの/されないもの

 そもそもビューステートがどのような場合に生成されるのか(されないのか)を確認しておきましょう。これを理解することで、ビューステートの必要性をより深く理解できるでしょう。

プログラムから生成された表示

 たとえば、Page_Loadイベントハンドラーで、Labelコントロールに対して初期値を設定した場合、その値はビューステートに保存されます(図2.39・リスト2.8)。

図2.39 ViewState.aspxのフォームレイアウト

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
  If Not Page.IsPostBack Then
    lblResult.Text = "こんにちは、世界!"
  End If
End Sub
リスト2.8 ViewState.aspx.vb

 これは、ボタンクリックでポストバックが発生した場合にも、ラベルの値を維持しなければならないためです。もしもビューステートがなければ、リスト2.8の太字の部分は初回にしか実行されないので、ポストバック時にラベルの値は消えてしまうはずです(図2.40)。

図2.40 ビューステートの仕組み

 先ほどはIsPostBackメソッドを利用して、初回ロード時とポストバック時の処理とを分岐する方法を紹介しましたが、実はこれには、

ビューステートによってデータが展開されているにもかかわらず、Page_Loadイベントハンドラーで重複して処理を行うことの無駄を防ぐ

という意味もあったわけです。

 ここでは単にリテラルをセットしているだけですので、処理上の無駄はそれほどではありません。しかし、Page_Loadイベントハンドラーでデータベースや外部サービスからページを生成している場合には、IsPostBackメソッドでポストバック時の処理を省くことで、処理を効率化できます。以下はASP.NETの定型的なコードですので、きちんと覚えておきましょう。

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
  If Not Page.IsPostBack Then
    ' データベース/外部サービスへのアクセス&表示処理
  End If
End Sub

プログラムから生成された表示(テキストボックス)

 リスト2.9(図2.41)は、プログラムから生成された値であるにもかかわらず、ビューステートに保存されない例です。

図2.41 ViewState2.aspxのフォームレイアウト

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
  If Not Page.IsPostBack Then
    txtResult.Text = "こんにちは、世界!"
  End If
End Sub
リスト2.9 ViewState2.aspx.vb

 TextBoxコントロール(フォーム要素)の値は、ポストバック時に放っておいてもサーバーに送信されるものなので、あえてビューステートに値を保存する必要がないのです。

* 誤解されやすいのですが、ポストバックの前後でテキストボックスなどの値が維持されるのビューステートの機能ではなく、普通にサーバーに送信されたテキストボックスの値がサーバーコントロールによって復元されているだけです。

 ただし、TextBoxコントロールにTextChangedイベントハンドラーを追加するとどうでしょう(中身は空でかまいません)。今度は「変更前」の値を維持しなければなりませんので、テキストボックスの値がビューステートに保存されるようになります。

保存されるのはテキストばかりではない

 ビューステートに保存されるのは、いわゆるテキストばかりではありません。リスト2.9の太字部分に、以下のような記述を追加してみるとどうでしょう。

txtName.BackColor = Drawing.Color.Aqua

 これは、ビューステートに保存されます。ビューステートとは、あくまでビュー(表示)に関わる情報すべてを管理するための仕組みなのです。

静的にプロパティを設定した場合

 たとえば、ラベルに初期値テキストを設定する際に、(プログラムからではなく)プロパティウィンドウから静的に設定した場合、その値はビューステートに保存されません。

<asp:Label ID="lblResult" runat="server">こんにちは</asp:Label>

 静的に宣言された値は、ビューステートで管理しなくても、コントロールツリーを再構築する時に常に復元されるためです。

ビューステートの注意点

 ビューステートは基本的にASP.NETが管理しているので、とりあえずは開発者が意識しなくてもアプリケーションは動作します。しかし、まったく意識しなくて良いというわけではありません。以下に、ビューステートの問題点と、その対応策を挙げておくことにします。

ポストバック時のオーバーヘッドが増大する

 繰り返しますが、ビューステートの正体は隠しフィールドです。ページが複雑になり、配置されたサーバーコントロールの数が多くなった場合(あるいは大量のデータを扱うコントロールが配置された場合)、ビューステートのサイズも比例して巨大になります。たとえば、図2.42はGridViewコントロール(グリッド表)を配置した場合のビューステートです。

図2.42 グリッド表と、自動生成されたビューステート

 比較的シンプルな表なのに、ビューステートは相当な量になっていることが確認できます。ビューステートが大きくなるということは、

ページの状態をシリアライズ&エンコード → ダウンロード → ポストバック → デコード&ページに反映

というそれぞれの段階でオーバーヘッドが増大することでもあるので、要注意です。

 この問題への対応策としては、ViewStateModeプロパティを利用することが挙げられます。ViewStateModeプロパティを利用することで、ビューステートの、

  • Enabled(有効)
  • Disabled(無効)
  • Inherit(親コントロールの設定を継承)

を変更できます。ページそのもののViewStateModeプロパティのデフォルトはEnabled、コントロール個々のデフォルトはInheritですので、もしもページ全体でビューステートを無効にするには、@Pageディレクティブで以下のように設定してください。

<%@ Page ViewStateMode="Disabled" ...%>

 たとえば、データベースに対して常に問い合わせを行っているようなコントロールでは、ビューステートは無効にすべきです。これによって、データベースの問い合わせによって上書きされるだけの「無駄な」ビューステートを省くことができます。

* ViewStateModeプロパティはASP.NET 4以降で利用できます。ASP.NET 3.5以前では、EnableViewStateプロパティでビューステートの有効/無効を設定してください。ASP.NET 4ではEnableViewStateプロパティはデフォルト値(True)から変更すべきではありません。EnableViewStateプロパティがFalseである場合、ViewStateModeプロパティでEnabled(有効)を設定しても、ビューステートは有効にはならないためです。

ビューステートに機密情報を含めない

 7.1節で改めて解説しますが、ビューステートには開発者が自ら値をセットすることもできます。その際に、いわゆる機密情報を含めるべきではありません。前述の注釈の中でも述べたように、ビューステートの内容をデコードするのは簡単なことであるからです。

 一応、@PageディレクティブのEnableViewStateMac属性をTrueにすることで、ビューステートに暗号化を施すことも可能ではあります。しかし、これはサーバーに対してポストバックのたびに暗号化/複号の負荷を強いるものでもあります(そして、これは決して小さくないオーバーヘッドです)。利用する場合には、パフォーマンスとの兼ね合いを十分に見ながら導入するようにしてください。

 まずは、ビューステートには機密情報はセットしないことを原則と考えるべきでしょう。

 次回は、「第7章 状態管理」から「7.1 ビューステート/7.2 クッキー/7.3 セッション情報」を転載します。end of article

 

 INDEX
  [書籍転載]独習ASP.NET 第3版
  ASP.NETを理解する3つの仕組み
    1.サーバーコントロール
    2.イベントドリブンモデル
  3.ビューステート

インデックス・ページヘ  「独習ASP.NET 第3版」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間