絶対絶命!もうクビ?ポストバックとビューステート障害

ASP.NETでフレームを使用したシステムで、発生した障害内容についてまとめます。


Ajaxを使用して一覧表示のパフォーマンスを改善するブログはこちら!

システム環境

Windows Server 2003 StandardEdition With ServicePack1 (OS)
InternetExplorer6 SP2
Visual Studio 2005(Service Pack1)⇒ASP.NET 2.0
IIS 6.0

障害内容

ポストバック障害

ポスト(Request要求)は行われるが、ポストバックイベントが発生しない。
(同じ操作で正常にポストバックイベントが発生したり発生しなかったりする)
また、ポスト時にIsPostBackプロパティの値がFalseになっていてポストバック
として認識されていない。
例)ポストバック時
  (ボタンクリックイベント、ドロップダウンのSelectedIndexChangedイベント)
・正常な動作
  Page_Loadメソッドが実行
  ボタンクリックイベントが発生
・障害時の動作
  Page_Loadメソッドのみ実行

ビューステート障害

ポストバック時に全WebControlの状態が初期状態に戻る(ビューステートが消失)
(同じ操作でビューステートが消失したり保持されたりする)
例)ポストバック時のasp:Labelやasp:buttunコントロールの情報
・正常な動作
  表示していた内容(値、背景色、文字色等)が保持されて表示される。
・障害時の動作
  表示していた内容(値、背景色、文字色等)が消失してデフォルト値に戻る。

ポストバックの仕組み

ボタンコントロール

ポストバックはコントロールのイベント検知用value値を参照して値が変更されていればイ
ベントが発生したものと扱います。
(ドロップダウン、リンクボタンやテキストボックスに当てはまるが通常のボタンは無関係)

ドロップダウンコントロール、リンクボタン等

JavaScriptで__doPostBack関数の第1引数にイベントが発生したコントロールID、第2引数
にコントロールの値を渡してポストさせて、コードビハインドページでイベントが発生し
たコントロールIDとイベントの種類を検知します。

以下はdoPostBack関数のサンプルです

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

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
	無題のページ
</title></head>
<body link="#000ce0">
    <form name="form1" method="post" action="Default.aspx" id="form1">
<div>
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" 
    value="/wEPDwULLTE4NDIzMDg2NDRkZP3kLJ7ET0ucGldWzRUtevIJ+CLA" />
</div>

<script type="text/javascript">
<!--
var theForm = document.forms['aspnetForm'];
if (!theForm) {
    theForm = document.aspnetForm;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}
// -->

ポストバック障害の原因

ポストバック障害と共に、ビューステートの消失障害が発生している。
(Request情報を参照したところ、ビューステートが消失していた。)
原因として以下の可能性を考えています。

原因その1

PostではなくGetでリクエスト要求がされている。
⇒この場合、ポストバックイベントは発生せず、ビューステートも消失する。
 ポストデータ全てが取得できなくなる。

原因その2

ポストバックではなくポストとしてリクエスト要求されている。
⇒この場合、ポストバックイベントは発生しない。
 ビューステートは初期表示時と同じ情報を保持。

原因その3

ビューステートが消失してイベントが発生したコントロールIDとイベントの種
類が判別不能になっている。
(コントロール値の以前の状態との差異を判断できない)
⇒この場合、ポストバックイベントは発生しない。

検証

原因その1

  ポストデータがコードビハインドページで参照可能なため異なる。

原因その2

  動作としては近い。
  ビューステートは消失したのではなく、コントロールを初期表示時の状態で表示している。
  ビューステートの消失は起きていない。

原因その3

  Buttunコントロールのポストバックイベントにはコントロールの以前の状
  態との差異は無関係なため異なる。


検証の結果、「原因その2」が原因ではないかと考えられる。
また、このケースの場合、ビューステート障害は発生していないと考えられる。
補足だが、ビューステートのデータ量が膨大である場合にポストができないという現象が
発生する事があるようだが、それほどビューステートのデータ量が多いわけではないので
原因ではないようだ。


結局ポストバック障害は正確な障害原因は分からずじまいですが、
ネットワーク、セキュリティの設定で同様の現象が起きるといった情報もネット
上に掲載されていました。

対応方法

他のシステムが動作している環境にシステムを構築するため、ネットワーク、セキュリティの
環境設定を変更することはできないため、以下の対応によりポスト発生時に明示的にイベント
発生時の処理を呼び出す事により、ポストバック障害の対応をしました。
なお、ビューステートはポストバックの度にコントロールに情報を再設定することにより対応をしました。
(コントロールを初期表示時の状態で表示しているため)

対応の詳細

1.イベントが起きたコントロールを保持
  JavaScriptで以下の処理を行う

  • OnClientClickイベントによりJavaScriptの関数を呼び出す
  • hiddenコントロールにイベントが起きたコントロールID(イベントの種類)を保存する
  • ポストを実行する(自動でポスト)


2.イベントが発生したコントロールのイベント処理を呼び出す
  コードビハインドページで以下の処理を行う    

以下ASPXファイルのサンプル(ボタンコントロールの対策)
<%@ Page Language="VB" CodeFile="default.aspx.vb" Inherits="default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>検索条件選択</title>
</style>
</head>
<script language ="javascript" type="text/javascript">
//コントロールのポストバック発生時
function btnExePost(btnID)
{
  document.getElementById('txtEvent').value = btnID;
}

//ドロップダウンコントロールの選択変更処理
function drpSelectChng(drpObj)
{
  //選択したインデックスを保存
  document.getElementById('txtSelectedIndex').value = document.getElementById(drpObj).selectedIndex;
  //選択変更したドロップダウンコントロールの保存
  document.getElementById('txtDrpSelChng').value = drpObj;
  //ドロップダウン選択変更イベント
  document.getElementById('btnDrpSelChng').click();
}
</script>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:Button ID="btnSearch" OnClientClick="btnExePost('btnSearch');" runat="server" Text="検索" />
    <select ID="drpEigyo" runat="server" onChange="drpSelectChng('drpEigyo')" ></select>
    </div>
    </form>
</body>
</html>
以下コードビハインドページのサンプル
  Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Dim isPostFlg As Integer = 0
    If Not Me.txtEvent.Value = "" Then
      If IsPostBack = False Then
        isPostFlg = 1
      End If
    Else
      isPostFlg = 2
      '一度画面が表示されたらセットする
      Me.txtEvent.Value = "Post"
    End If

    '0:ポストバック処理(正常) 1:ポストバック処理(異常),2:初期表示時
    Select Case isPostFlg
      Case 1
        Select Case Me.txtEvent.Value
          Case "btnSearch"
            btnSearch_Exe()
          Case "btnDrpSelChng"
            btnDrpSelChng_Exe()
              ・
              ・
              ・
        End Select
        Me.txtEvent.Value = "Post"
      Case 2
        初期表示時の処理を記述
              ・
              ・
              ・
    End Select
  End Sub

  'ポストバック処理(正常:Page_LoadイベントでisPostFlg=0)
  Protected Sub MultiButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSearch.Click, _
                                                                                                 btnDrpSelChng.Click
    Dim aControlID As String = CStr(CType(sender, Button).ID)
    Select Case aControlID
      Case "btnSearch"
        btnSearch_Exe()
      Case "btnDrpSelChng"
        btnDrpSelChng_Exe()
           ・
           ・
           ・
    End Select
  End Sub