ASP.NET UserControls での未処理の例外のキャッチ
-
08-06-2019 - |
質問
ユーザー コントロールを動的に読み込み、Web フォームの Controls コレクションに追加しています。
レンダリング中にハンドルされない例外が発生した場合、ユーザー コントロールを非表示にしたいと考えています。
そこで、各 UserControl の Error イベントにフックしようとしましたが、このイベントは Page クラスの場合のように UserControl に対しては決して起動しないようです。
グーグルで調べてみましたが、期待できるものではないようです。何かアイデアはありますか?
解決
mmic、からの続き あなたの返答 わたしの 以前のアイデア..
追加のロジックは必要ありません。それが重要です。問題のクラスには何もせず、インスタンス化のバブルラップでクラスを包むだけです。:)
さて、箇条書きのつもりでしたが、この作品を自分の目で見てみたかったので、いくつかまとめてみました。 とても 大まかなコードですが、コンセプトはあり、機能するようです。
長文のお詫び
セーフローダー
これは基本的に、先ほど述べた「バブル」になります。コントロール HTML を取得し、レンダリング中に発生するエラーをキャッチします。
public class SafeLoader
{
public static string LoadControl(Control ctl)
{
// In terms of what we could do here, its down
// to you, I will just return some basic HTML saying
// I screwed up.
try
{
// Get the Controls HTML (which may throw)
// And store it in our own writer away from the
// actual Live page.
StringWriter writer = new StringWriter();
HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
ctl.RenderControl(htmlWriter);
return writer.GetStringBuilder().ToString();
}
catch (Exception)
{
string ctlType = ctl.GetType().Name;
return "<span style=\"color: red; font-weight:bold; font-size: smaller;\">" +
"Rob + Controls = FAIL (" +
ctlType + " rendering failed) Sad face :(</span>";
}
}
}
そしていくつかのコントロール..
ここでは 2 つのコントロールを一緒にモックしました。1 つはジャンクをスローし、もう 1 つはジャンクをレンダリングします。ここで言っておきますが、私は何も気にしません。これらはカスタム コントロールに置き換えられます。
悪いコントロール
public class BadControl : WebControl
{
protected override void Render(HtmlTextWriter writer)
{
throw new ApplicationException("Rob can't program controls");
}
}
グッドコントロール
public class GoodControl : WebControl
{
protected override void Render(HtmlTextWriter writer)
{
writer.Write("<b>Holy crap this control works</b>");
}
}
ページ
OK、それでは「テスト」ページを見てみましょう。ここでは、単にコントロールをインスタンス化し、その HTML を取得して出力します。その後、デザイナーのサポートなどについて考えます。
ページのコードビハインド
protected void Page_Load(object sender, EventArgs e)
{
// Create some controls (BadControl will throw)
string goodHtml = SafeLoader.LoadControl(new BadControl());
Response.Write(goodHtml);
string badHtml = SafeLoader.LoadControl(new GoodControl());
Response.Write(badHtml);
}
思い
OK、あなたが考えていることはわかります。「これらのコントロールはプログラムでインスタンス化されていますが、デザイナーのサポートはどうなるのでしょうか?」私はこれらのコントロールをデザイナーにとって使いやすいものにするのに何時間も費やしましたが、今ではあなたは私のモジョを台無しにしています。」
OK、まだこれを実際にテストしていません (おそらくすぐにテストできます!) が、ここでのアイデアは、ページの CreateChildControls メソッドをオーバーライドし、フォームに追加された各コントロールのインスタンスを取得し、SafeLoader を通じて実行することです。コードが合格した場合は、通常どおり Controls コレクションに追加できますが、そうでない場合は、誤ったリテラルなどを作成してもかまいません。
ついに..
繰り返しになりますが、長い投稿で申し訳ありませんが、ここでコードを取得したかったので、これについて話し合うことができました:)これが私のアイデアを示すのに役立つことを願っています:)
アップデート
デザイナーにコントロールを取り込み、これで CreateChildControls メソッドをオーバーライドすることでテストしましたが、正常に動作します。見栄えを良くするためにいくつかのクリーンアップが必要になるかもしれませんが、それはあなたにお任せします ;)
protected override void CreateChildControls()
{
// Pass each control through the Loader to check
// its not lame
foreach (Control ctl in Controls)
{
string s = SafeLoader.LoadControl(ctl);
// If its bad, smack it downnnn!
if (s == string.Empty)
{
ctl.Visible = false; // Prevent Rendering
string ctlType = ctl.GetType().Name;
Response.Write("<b>Problem Occurred Rendering " +
ctlType + " '" + ctl.ID + "'.</b>");
}
}
}
楽しむ!
他のヒント
これは興味深い問題ですね。カスタム コントロールなどに関しては、私はまだかなり新鮮ですが、私の考えは次のとおりです (お気軽にコメント/修正してください!)。(私はここでちょっと考えたり、大声で書いたりしています!)
- 場合によってはレンダリング中にエラーが発生しても手遅れではないでしょうか?(一部のコントロール HTML はすでに Writer と出力に送信されている可能性があるため)。
- したがって、ユーザー コントロールの Render メソッドをラップするのが最善ではありません。ただし、「ライブ」HtmlTextWriter への参照を渡すのではなく、すべてがうまくいけば、独自の例外を渡し、この小さな安全な「バブル」で発生した例外をトラップします。 、その後、結果の HTML を実際の HtmlTextWriter に渡しますか?
- このロジックはおそらく、実行時にコントロールを動的にロード/レンダリングするために使用する汎用ラッパー クラスに渡すことができます。
- エラーが発生した場合でも、必要な情報はすべて自由に利用できます。(つまり、コントロール参照など)。
私の考えだけ、燃え尽きろ!:D;)
エラーが発生している場所に応じて、次のようなことができます...
public abstract class SilentErrorControl : UserControl
{
protected override void Render( HtmlTextWriter writer )
{
//call the base's render method, but with a try catch
try { base.Render( writer ); }
catch ( Exception ex ) { /*do nothing*/ }
}
}
次に、UserControl の代わりに SilentErrorControl を継承します。
Global.asax と Application_Error?
http://www.15秒.com/issue/030102.htm
または、個々のページのみの Page_Error イベント:
http://support.microsoft.com/kb/306355
void Page_Load(object sender, System.EventArgs e)
{
throw(new ArgumentNullException());
}
public void Page_Error(object sender,EventArgs e)
{
Exception objErr = Server.GetLastError().GetBaseException();
string err = "<b>Error Caught in Page_Error event</b><hr><br>" +
"<br><b>Error in: </b>" + Request.Url.ToString() +
"<br><b>Error Message: </b>" + objErr.Message.ToString()+
"<br><b>Stack Trace:</b><br>" +
objErr.StackTrace.ToString();
Response.Write(err.ToString());
Server.ClearError();
}
また、Karl Seguin (こんにちは Karl!) は、代わりに HttpHandler を使用することについて投稿しています。
http://codebetter.com/blogs/karlseguin/archive/2006/06/12/146356.aspx
(転載の許可が何なのかはわかりませんが、回答を書きたい場合は、私の賛成票を獲得しました ☺)
レンダリング メソッドとロード メソッドをエラー処理する UserControl の新しいサブクラスを追加し (希望どおりに非表示にできるように)、それをユーザー コントロール用に継承してみてはどうでしょうか?
理解できるかどうかわかりません あなたの返答..コントロールをどのように読み込み、コントロール コレクションに追加していますか?
これが、「更新」セクションに追加されたビットの要点です。を柔軟に使用できます。 セーフローダー どこでもお好きなように。
HTML へのアクセスや制御ができないと感じている理由がわかりません。SafeLoader の目標は、次のことを行わないことです。 お手入れ HTML が何であるかについては、単純にコントロール (「バブル」内) を「出力」して、現在の状態で正常に読み込まれるかどうかを判断します。
そうであれば(すなわち、HTML が返される)、それを使って好きなことを実行したり、HTML を出力したり、コントロールをコントロール コレクションに追加したりすることができます。
そうでない場合は、エラー メッセージを表示したり、カスタム例外をスローしたりするなど、好きなことを行うことができます。選択はあなた次第です!
これがあなたの問題を明確にするのに役立つことを願っていますが、そうでない場合は、叫んでください:)
私は @Keith のアプローチを使用しましたが、問題は、例外がスローされるまでコントロールがレンダリングされ、HTML タグが開かれる可能性があることです。デバッグ モードの場合は、コントロールに例外情報もレンダリングします。
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
try
{
// Render the module to a local a temporary writer so that if an Exception occurs
// the control is not halfway rendered - "it is all or nothing" proposition
System.IO.StringWriter sw = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htw = new System.Web.UI.HtmlTextWriter(sw);
base.Render(htw);
// We made it! Copy the Control Render over
writer.Write(sw.GetStringBuilder().ToString());
}
catch (System.Exception ex)
{
string message = string.Format("Error Rendering Control {0}\n", ID);
Log.Error(message, ex);
if (Page.IsDebug)
writer.Write(string.Format("{0}<br>Exception:<br><pre>{1}\n{2}</pre>", message, ex.Message, ex.StackTrace));
}
}