- PR -

[ASP.NET] ViewStateは無効です。に対するアプローチについて

投稿者投稿内容
いのつち
ベテラン
会議室デビュー日: 2002/05/14
投稿数: 73
投稿日時: 2003-04-11 12:34
いつもお世話になっております。

[ViewStateは無効です。壊れている可能性があります。]
のエラーが発生した場合に、ユーザに対する対応方法を考えているのですが、
お知恵を貸してください。

上記のエラーが発生する場合は
1. 本当に不正なデータが送られた
(古いブラウザなどが"+"をうまく処理できないケースがありました。
・または本当に改ざんしている)
2. アプリケーションがリスタートした場合
(DLLを更新・ASP.NETがメモリオーバなどで自動再起動など)
再起動される前にアクセスされたブラウザの情報で、再起動後にPOSTBACKされる。

が考えられます。他の場合もあるかも。。
大半は2.のケースで発生しています。


どのページでも発生し得るので、
Global.asax の OnErrorで HttpExceptionを捕らえてフォローしようと考えています。


1.HttpException例外が発生されるようなのですが、
 HttpExceptionの中でも「ViewStateは無効です。」の場合だけ、
 対応したい場合判断はどのようにすればよいでしょうか。
 ExceptionのMessageで判断するしかないのでしょうか。
 (例外のメッセージだけでは、ユーザには理解しがたいので、
  補足の説明を加えたいと考えています。)

2.対応方法を次のように考えています。
 A.エラーに対する説明を表示する
 B.表示の中に呼び出されたURLをPOSTでなく、再度GETから読むように 
  Request.Urlの情報へのリンクを加える。
 
 メッセージの説明としては、上記エラー発生原因をもう少し噛み砕いた内容で記述するつもりですが、そもそもエラーの原因として、私の認識に不足はないでしょうか。

またみなさんは、どのようになされていますか?
AOPP
ベテラン
会議室デビュー日: 2002/11/18
投稿数: 66
投稿日時: 2003-04-24 22:58
aoppです。

質問の答えになっていないのですが、1点質問があります。

「古いブラウザなどが"+"をうまく処理できないケースがありました。 」
上記は、name="__VIEWSTATE" value="dDwtNTMwNzcx+”に"+"があるときですか!!!
あと古いブラウザとは、
逆に質問で申しわけありません。

machine.configの<customErrors>で、エラーステータス(HTTP)を使用し、エラー対応.aspxを振り分けていますが、確かにフォローするには、ExceptionのMessage内の文字列で判断するしかないかも


Access
ぬし
会議室デビュー日: 2002/04/08
投稿数: 829
投稿日時: 2003-04-26 07:30
引用:

メッセージの説明としては、上記エラー発生原因をもう少し噛み砕いた内容で記述するつもりですが、そもそもエラーの原因として、私の認識に不足はないでしょうか。



ViewState破壊のエラーは、ご指摘の2)以外でも発生すると思われます。
たとえば、HTMLの__VIEWSTATEが破壊されているときなどが該当します・・・

私のサイトでは、2)の条件に該当しないのに1日に数回発生しています。

なぜか、TCPが破壊されたHTMLを認識しないで渡すことがあるようです。
この辺をもう少し調査してみたらどうでしょうか・・・・

エラーが発生ときの対処よりも、エラー発生の原因とエラーを発生させないような
対処が必要と思われます。

対処方法:
1) ViewStateに保存する情報を必要最小限にする
DataGridなどを使用するときは、EnableViewState="False"を適用する。

2) ViewStateの代わりにSession変数に保存する

個人的には、2)がお勧めです。

この場合、
LoadPageStateFromPersistenceMedium() と
SavePageStateToPersistenceMedium() を
Overrideすれば可能と思います。



いのつち
ベテラン
会議室デビュー日: 2002/05/14
投稿数: 73
投稿日時: 2003-04-28 16:04
ご回答いただきありがとうございます。

引用:

「古いブラウザなどが"+"をうまく処理できないケースがありました。 」
上記は、name="__VIEWSTATE" value="dDwtNTMwNzcx+”に"+"があるときですか!!!
あと古いブラウザとは、



古いというのは、語弊があるかもしれません。
iBoxというセットトップボックスと、i-modeのエミュレータActiveMascotで発生しました。
[1+1] と送信すると、 [1 1]と送られていました。

引用:

私のサイトでは、2)の条件に該当しないのに1日に数回発生しています。

なぜか、TCPが破壊されたHTMLを認識しないで渡すことがあるようです。
この辺をもう少し調査してみたらどうでしょうか・・・・



貴重な情報ありがとうございます。
私のところでも、1日に数件、このエラーが発生しています。
以前は、先にあげたケースだったのですが、最近は、そうとも限らない状況
もありましたので、どうしたらよいか苦慮しておりました。

また、なかなか再現性のある現象でもなく、開発環境ではまず発生しない
ケースなものですから。


ViewStateの制御を見直してみたいとおもいます。
Sessionだと便利ですが、かなりデータが大きくならないかちょっと心配。



Ten.
ベテラン
会議室デビュー日: 2003/04/03
投稿数: 67
投稿日時: 2003-05-08 10:52
最近ASP.NETを始めたばかりなので勘違いがあるかもしれませんが、よろしくお願いします。

引用:

2. アプリケーションがリスタートした場合
(DLLを更新・ASP.NETがメモリオーバなどで自動再起動など)
再起動される前にアクセスされたブラウザの情報で、再起動後にPOSTBACKされる。


私の環境ではページを表示した後にIISを再起動し、そしてPostBackしても特にエラーにはなりません。
何か条件でもあるのでしょうか?

また、Global.asaxでのエラートラップですが、以下の方法で主要原因の例外を取得しても「ViewStateは無効です。」を判断できないのでしょうか?
Server.GetLastError().GetBaseException()
いのつち
ベテラン
会議室デビュー日: 2002/05/14
投稿数: 73
投稿日時: 2003-05-09 19:36
引用:

zakeyさんの書き込み (2003-05-08 10:52) より:
私の環境ではページを表示した後にIISを再起動し、そしてPostBackしても特にエラーにはなりません。
何か条件でもあるのでしょうか?



 私も自身で意図的に操作しても、同様の例外は発生しないのです。
 未処理の例外が発生した場合に、メールで例外の内容を
 管理者に通知するようにしているのですが
 先の事例2のケースの時にアクセスされていたユーザのリクエストで発生します。
 (すべてのユーザで発生するのではなく、何らかの条件が重なったときに一部のユーザの
リクエストで発生しているのだとおもいます。詳しくは私もわかっていません。)
 
引用:

また、Global.asaxでのエラートラップですが、以下の方法で主要原因の例外を取得しても「ViewStateは無効です。」を判断できないのでしょうか?
Server.GetLastError().GetBaseException()


はい。これで取得できるのは、HttpExceptionが取得できるのですが、
HttpExceptionの発生条件は他にもありますので、中でもViewStateエラーの場合の判別
方法というのがあれば、知りたいところであります。
いのつち
ベテラン
会議室デビュー日: 2002/05/14
投稿数: 73
投稿日時: 2003-05-09 19:44
Mr.ADO.NETさんに紹介いただいた方法を実装してみました。
Sessionに保持するのではなく、サーバのファイルに保存しています。
負荷・ディスクスペースなどはこれから監視しながら、さらに検討していくつもりです。

作成した内容をご紹介まで。

Pageクラスを継承した ViewStateFilePageクラスを作成し、必要な ページでの継承を
Pageクラスではなく、ViewStateFilePageクラスを使用しています。

-------------------------------------------------------------------
/// <summary>
/// ViewStateの保存先をユーザへのHTMLレスポンスではなく、
/// サーバ上のディスクに保存するページオブジェクト
/// </summary>
public class ViewStateFilePage : System.Web.UI.Page
{
public ViewStateFilePage() :base()
{
}

/// <summary>
/// ViewState値の保存先のファイル名を取得します。
/// Web.Config で設定された VIEWSTATE_PATH キーの値 / AppDomainのHash値/ SessionID値 / パス名(パスの[/]は[_]に置き換えられます)
/// のファイル名を取得します。
///
/// </summary>
/// <returns></returns>
protected string GetViewStateFileName()
{
string dir = Server.MapPath(string.Format("{0}{1}",System.Configuration.ConfigurationSettings.AppSettings["VIEWSTATE_PATH"],AppDomain.CurrentDomain.GetHashCode()));
if(!Directory.Exists(dir))
Directory.CreateDirectory(dir);
dir = dir + "\\" + Session.SessionID;
if(!Directory.Exists(dir))
Directory.CreateDirectory(dir);

return string.Format("{0}\\{1}.txt",dir,Request.CurrentExecutionFilePath.Replace("/","_"));


}

/// <summary>
/// ViewStateの読込先をディスクから読み込むようにオーバライドします。
/// ファイルの読取に失敗した場合は、null値を返します。
/// </summary>
/// <returns></returns>
protected override object LoadPageStateFromPersistenceMedium()
{
string path = GetViewStateFileName();
System.Web.UI.LosFormatter los = new LosFormatter();
try
{
using(Stream s = new FileStream(path,FileMode.Open))
{
return los.Deserialize(s);

}
}
catch(System.IO.IOException)
{
return null;
}
}

/// <summary>
/// ViewStateの保存先をファイルに書き込むようにオーバライドします。
/// ファイルの書き込みに失敗した場合は、ViewStateの値は失われます。
/// </summary>
/// <param name="viewState"></param>
protected override void SavePageStateToPersistenceMedium(object viewState)
{
LosFormatter los = new LosFormatter();
string path = GetViewStateFileName();
try
{

using(Stream s = new FileStream(path,FileMode.Create,FileAccess.Write))
{
los.Serialize(s,viewState);
}
}
catch(System.IO.IOException)
{
}
}
}
Access
ぬし
会議室デビュー日: 2002/04/08
投稿数: 829
投稿日時: 2003-05-12 04:41
ブラウザの「戻る」ボタンを有効にする場合、このサンプルの仕様ですと
常に最新のViewStateが復元されると思うのです・・・

__VIEWSTATEにIDを埋め込んでおいて、ID別にViewStateを保存する方法も
あります。

たとえば、「戻る」ボタンを5回まで許可するときは、5世代分の
IDを管理します。ViewStateを復元するときは、__VIEWSTATEに格納されている
IDを基に復元します。


スキルアップ/キャリアアップ(JOB@IT)