WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

ViewState の構造

by WebSurfer 2011年8月2日 23:55

ViewState オブジェクトのパース結果

HTTP 要求および HTTP 応答には本質的に状態(英語で state)がありませんが、ASP.NET ではポストバックと ViewState という機能を利用して状態情報をやり取りして、Windows アプリのようなイベントドリブンな Web アプリを実現しています。

ViewState は、サーバーから送信されてくる html コードの中の隠しフィールド(type 属性が "hidden" に設定された要素)に含まれています。内容は、サーバーからクライアントに送信された時点のページの状態情報(コントロールのプロパティ値など)です。

ポストバックとは、ブラウザが表示している同一ページに HTTP POST 要求を送信することです。ポストバックがかかると、ブラウザからは form のデータと ViewState のデーターがサーバーに送信され、同一ページの再描画・再送信が要求されます。

要求を受けたサーバーはメモリにそのページをロードし、ViewState の情報(即ち、前回送信時のページの状態情報)を用いてコントロールのプロパティを前回送信時の値に設定します。その後、ポストされた form データ(例:ユーザーが入力した TextBox.Text の値)と ViewState から再生されたプロパティ値(例:前回送信時の TextBox.Text の値)を比較して変更系イベントを発生させています。

ViewState に関する注意点、どのような構造になっているか、シリアライズ/デシリアライズなどをどこでどのように行っているかなどにつき、ポイントと思う点を備忘録として書いておきます。

  1. POST された form のデータ(例:ユーザーが入力した TextBox.Text の値など)は ViewState には含まれません。
  2. ViewState は動的に生成されたコントロールそのものを再生成することはできません。(ただし、そのプロパティ値は、先の記事 変更系イベント発生のメカニズム で書きましたように、Load イベント後の LoadViewState メソッドで設定されます。)
  3. ポストバック前後で動的に設定が変わるプロパティや、バインドするデータ、ユーザーインターフェイスがなければ、コントロールは ViewState を保持しません。(例えば、静的にページに配置した TextBox の Text プロパティがポストバック前後で空白のままであれば、ViewState には何も保持されません。ユーザーが何か入力して POST すると、その応答で初めて ViewState に先にユーザーが入力したデータが保持されます。)
  4. ViewState は Base64 エンコーディングされますが、デフォルトでは暗号化はされません。ただし、改ざん防止のため鍵付きハッシュが付与されています。詳しくは、Webアプリケーション開発技術大全の ViewStateのセキュリティ を参照ください。

    @ Page ディレクティブの ViewStateEncryptionMode 属性を Always に設定すると ViewState 情報は常に暗号化されるということですが、性能への影響があるので止めておいた方が良さそうです。そもそも暗号化が必要なデータは ViewState ではなく Session に格納するのが基本で、ViewState を暗号化しなければならないケースはあまりなさそうです。ViewStateEncryptionMode 属性はデフォルトの Auto のままにしておいて、どうしても ViewState の暗号化が必要なページのみPage.RegisterRequiresViewStateEncryption メソッドを使って暗号化するべきと思います。
  5. ASP.NET 2.0 では、ViewState とは別に、ControlState と呼ばれる別個のオブジェクトにサーバーコントロールが機能するために必要な状態情報が格納されます。開発者が ViewState を無効にしても ControlState は影響を受けません。従って、正確には、状態情報には ViewState と ControlState の両方が含まれます。
  6. 状態情報は、サーバーから送信される際は Page.SavePageStateToPersistenceMedium(Object) メソッド で保存され、ポストバックされた際は Page.LoadPageStateFromPersistenceMedium メソッド で再生されます。
  7. シリアライズされる前の ViewState + ControlState オブジェクトは LOS (Limited Object Serialization) 形式というらしいです。このオブジェクトのシリアライズ/デシリアライズは LosFormatter クラス により行われます。
  8. ViewState + ControlState オブジェクトは、System.Web.UI.Triplet、System.Web.UI.Pair、System.Collections.ArrayList などをノードとするツリー構造になっています。ViewState: All You Wanted to Know というページに、ViewState を表示/パースするサンプルコードがあります。このコードを使って、パースした結果を示したのが、上の画像です(一部分のみです。クリックすると拡大画面が表示されます)。ルートの一つ下の Pair の First(画像で '-2110287577')が ControlState、Second(5 行目の Pair 以下)が ViewState となっているようです。

上の画像のパース結果を表示したコードは以下のとおりです。ViewStateParserPage クラスは Page を継承したもので、中身のコードは ViewState: All You Wanted to Know のページの Listing 6 の通りです。

<%@ Page Language="C#"
    Inherits="ViewStateParserPage"    
    Trace="true" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:SqlDataSource ID="SqlDataSource1" 
      runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>"             
      SelectCommand=
        "SELECT TOP 2 [CustomerID], [ContactName], [ContactTitle] 
        FROM [Customers]">
    </asp:SqlDataSource>
    <asp:GridView ID="GridView1" 
      runat="server" 
      AutoGenerateColumns="False" 
      DataKeyNames="CustomerID" 
      DataSourceID="SqlDataSource1" 
      EnableViewState="True">
      <Columns>
        <asp:BoundField 
          DataField="CustomerID" 
          HeaderText="CustomerID" 
          ReadOnly="True" 
          SortExpression="CustomerID" />
        <asp:BoundField 
          DataField="ContactName" 
          HeaderText="ContactName" 
          SortExpression="ContactName" />
        <asp:BoundField 
          DataField="ContactTitle" 
          HeaderText="ContactTitle" 
          SortExpression="ContactTitle" />
      </Columns>
    </asp:GridView>
  </div>
  </form>
</body>

その他 ViewState に関する詳細は、ViewState をキーワードに検索すれば参考になる記事が多数見つかりますので、そちらを見てください。(手抜きです(笑))

Tags: ,

ASP.NET

アップロードされたファイルの一時保存先

by WebSurfer 2011年8月1日 23:01

サーバーにアップロードされたファイルがディスクに書き込まれる前に、サーバーのどこに一時保存されるでしょうか?

アップロードしたファイルの一時保存先

メモリかと思っていましたが、そうではなくて、ある値を超えるとディスクにバッファリングされるそうです。

その「ある値」というのは、httpRuntime 要素の requestLengthDiskThreshold 属性の 設定値で、デフォルト値はフォームやアップロードされたファイルすべてを含めて .NET 3.5, 4 の場合 80KB、.NET 2.0, 3.0 の場合は 256 バイトです(下の注記参照)。

以前、フォーラムで、「FileUpload を Session に保存しておいて、後で Save しようとしたが、ファイルサイズが大きいと失敗する。そのサイズの限度が大体 80KB」という報告があって、その原因究明に悩んだという話がありました。ディスクにバッファリングされることを知って理由が分りました。どうやらディスクに一時保存されたファイルは、サーバーが応答を返した後、消去されてしまうようです。

80KB 程度でディスクにバッファリングするのは効率が悪そうですが、同時に多数のユーザーがファイルをアップロードするようなサイトでは、メモリが分断されてすぐにメモリ不足になってしまうそうです。

逆に、めったに大きなファイルのアップロードはないサイトなら、ディスクにバッファリングされないように requestLengthDiskThreshold 属性の設定値を大きくしておく方がよさそうです。ちなみに、このブログでは 16MB に設定してあります。

<注>

デフォルト値は、以下のように MSDN ライブラリに書いてあることがいろいろ違っていて、イマイチはっきりしないので注意してください。

FileUpload クラス:要求の処理中にアップロードするファイルをメモリ内とサーバー上のどちらに一時的に格納するかを制御するには、httpRuntime 要素の requestLengthDiskThreshold 属性を設定します。この属性を設定すると、入力ストリームバッファーのサイズを管理できます。既定値は 256 バイトです。

HttpPostedFile クラス:既定では、フォームフィールドやアップロードされたファイルを含めて、サイズが 256KB を超えるすべての要求は、サーバーのメモリにではなくディスクにバッファーされます。

HttpRuntimeSection クラスの RequestLengthDiskThreshold プロパティ:入力ストリーム バッファリングのしきい値を示すバイト数。 既定値は 256 バイトです。

httpRuntime 要素の requestLengthDiskThreshold 属性:入力ストリームのバッファリングしきい値の限界値を KB 単位で指定します。 この値は、maxRequestLength 属性を超えないようにします。この属性は .NET Framework 2.0 で新たに追加されました。既定値は、80KB です。(左記は .NET 3.5, 4 の説明です。.NET 2.0, 3.0 の説明では「既定値は 256 です」となっています)。

実際に HttpRuntimeSection.RequestLengthDiskThreshold プロパティでデフォルト値を取得してみたところ、.NET 3.5, 4 の場合 80 でした(上の画像参照)。

MSDN ライブラリの RequestLengthDiskThreshold プロパティの説明では "プロパティは、入力ストリームのバッファリングのしきい値をバイト数で指定します" となっています(KB ではなくて)。でも、実際は 80 バイトではなくて、80KB が正解のように思われます。

Tags:

Upload Download

submit せずに IE の Auto Complete に登録

by WebSurfer 2011年7月23日 14:09

IE にはオートコンプリートという機能があって、TextBox に前回入力した文字列を入力中に補完してくれます(Firefox など他のブラウザにも同等の機能がありますが、今回は IE に限った話です)。

IE のオートコンプリート機能

TextBox に入力した文字列が IE のオートコンプリートに登録される条件は、その TextBox が form の中にあって、form が submit タイプのボタン(例: <input type="submit" ... )のクリックで submit されることです。

button タイプのボタン(例: <input type="button" ... )の onclick 属性にスクリプトを設定して、そのスクリプトで form を submit しても、オートコンプリートには登録されません。

詳しくは、マイクロソフトサポートオンラインのページ BUG: AutoComplete Does Not Work When You Use Script to Submit a Form を参照してください。

ただし、上記のページにも出ていますが、AutoCompleteSaveForm Method を利用すると、submit せずにオートコンプリートに登録することができます。

具体的なコード例は以下の通りです。(上記の AutoCompleteSaveForm Method のコードを ASP.NET の TextBox を使って書き換えただけです)

なお、AutoCompleteSaveForm メソッドは IE5 以降のバージョン専用だそうですので注意してください。もちろん、Firefox, Chrome などでは動きません。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
  <script type="text/javascript">
  //<![CDATA[
    function fnSaveForm() {
      window.external.AutoCompleteSaveForm(form1);
      document.getElementById("AutoCompleteTest").value = "";
      document.getElementById("AutoCompleteIgnore").value = "";
    }
  //]]>
  </script>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    This text is saved: 
    <asp:TextBox ID="AutoCompleteTest" 
      runat="server">
    </asp:TextBox>
    <br />
    This text is not saved:
    <asp:TextBox ID="AutoCompleteIgnore" 
      runat="server" 
      AutoCompleteType="Disabled">
    </asp:TextBox>
    <br />
    <input id="Button1" 
      type="button" 
      value="Save Value" 
      onclick="fnSaveForm()" />
  </div>
  </form>
</body>
</html>

Tags: ,

JavaScript

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar