捕获 ASP.NET UserControls 中未处理的异常
-
08-06-2019 - |
题
我动态加载用户控件,将它们添加到 Web 表单的 Controls 集合中。
如果用户控件在渲染时导致未处理的异常,我想隐藏它们。
因此,我尝试挂钩每个 UserControl 的 Error 事件,但似乎 UserControl 永远不会像 Page 类那样触发该事件。
谷歌搜索了一下,似乎没有希望。这里有什么想法吗?
解决方案
不需要额外的逻辑!这就是重点,您对相关类不执行任何操作,只是将它们包装在一些实例化气泡包装中!:)
好吧,我本来只是要点,但我想亲眼看看这项工作,所以我拼凑了一些 非常 代码很粗糙,但概念已经存在并且似乎有效。
为长篇文章道歉
安全加载器
这基本上就是我提到的“泡沫”。它将获取控件 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>";
}
}
}
和一些控件..
好吧,我只是在这里模拟了两个控件,一个会抛出垃圾,另一个会渲染垃圾。说到这里,我才不管呢。这些将替换为您的自定义控件。
坏控制
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>");
}
}
这一页
好的,让我们看看“测试”页面..在这里,我简单地实例化控件,抓取它们的 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);
}
想法
好的,我知道你在想什么,“这些控件是通过编程实例化的,那么设计师的支持呢?我花了很多时间为设计师设计这些控件,现在你却在扰乱我的魔力”。
好的,所以我还没有真正测试过这个(可能一分钟就能完成!)但这里的想法是重写页面的 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 方法,而不是向其传递对“Live”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*/ }
}
}
然后继承SilentErrorControl而不是UserControl。
Global.asax 和 Application_Error?
http://www.15seconds.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));
}
}