絶対絶命!もうクビ?ポストバックとビューステート障害
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メソッドのみ実行
ポストバックの仕組み
ボタンコントロール
ポストバックはコントロールのイベント検知用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
ポストバックではなくポストとしてリクエスト要求されている。
⇒この場合、ポストバックイベントは発生しない。
ビューステートは初期表示時と同じ情報を保持。
検証
原因その1
ポストデータがコードビハインドページで参照可能なため異なる。
原因その2
動作としては近い。
ビューステートは消失したのではなく、コントロールを初期表示時の状態で表示している。
ビューステートの消失は起きていない。
対応方法
他のシステムが動作している環境にシステムを構築するため、ネットワーク、セキュリティの
環境設定を変更することはできないため、以下の対応によりポスト発生時に明示的にイベント
発生時の処理を呼び出す事により、ポストバック障害の対応をしました。
なお、ビューステートはポストバックの度にコントロールに情報を再設定することにより対応をしました。
(コントロールを初期表示時の状態で表示しているため)
対応の詳細
1.イベントが起きたコントロールを保持
JavaScriptで以下の処理を行う
- OnClientClickイベントによりJavaScriptの関数を呼び出す
- hiddenコントロールにイベントが起きたコントロールID(イベントの種類)を保存する
- ポストを実行する(自動でポスト)
2.イベントが発生したコントロールのイベント処理を呼び出す
コードビハインドページで以下の処理を行う
- JavaScriptで設定したhiddenコントロールよりイベントが起きたコントロールを特定する
- コントロールのイベント処理を呼び出し処理を行う
以下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