在ASP.NET UserControls中捕获未处理的异常

时间:2008-08-14 08:06:59

标签: asp.net exception user-controls

我正在动态加载用户控件,将它们添加到Web表单的Controls集合中。

我想隐藏用户控件,如果它们在渲染时导致未处理的异常。

所以,我尝试连接到每个UserControl的Error事件,但似乎这个事件永远不会像为Page类那样触发UserControls。

有些谷歌搜索,似乎没有前途。这里有什么想法吗?

7 个答案:

答案 0 :(得分:12)

mmilic,继续your responseprevious idea ..

无需额外的逻辑!这就是重点,你对所讨论的类没有任何作用,只是将它们包装在一些实例化的bubble-wrap中! :)

好的,我只是要点点但是我想看到这个工作对我自己,所以我拼凑了一些非常粗略的代码,但概念就在那里,它似乎工作。

长篇文章

SafeLoader

这基本上就是我提到的“泡沫”..它将获取控件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>";
        }
    }
}

和一些控制..

好吧我只是在这里嘲笑两个控件,一个会抛出另一个会渲染垃圾。点这里,我不给废话。这些将被您的自定义控件替换..

BadControl

public class BadControl : WebControl
{
    protected override void Render(HtmlTextWriter writer)
    {
        throw new ApplicationException("Rob can't program controls");
    }
}

GoodControl

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>");
        }
    }
}

享受!

答案 1 :(得分:4)

这是一个有趣的问题..在自定义控件等方面,我仍然很新鲜,但这是我的想法(随意评论/纠正人们!)..(我有点想/在这里大声写出来!)

  • 如果在渲染过程中发生错误,在某些情况下,是否会为时已晚? (因为某些控件HTML可能已经发送到Writer并输出了。)
  • 因此,最好不要将用户控件的Render方法包装起来,而是将其传递给“Live”HtmlTextWriter,而不是传递给你自己的那个,捕获在这个小安全“泡沫”中引发的任何异常,如果一切顺利,然后将结果HTML传递给实际的HtmlTextWriter?
  • 这个逻辑可能会被挂到一个通用的包装类中,您可以在运行时动态加载/呈现控件。
  • 如果发生任何错误,您可以获得所需的所有信息! (即控制参考等)。

只是我的想法,火焰! :D;)

答案 2 :(得分:1)

根据您的错误发生位置,您可以执行类似......

的操作
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。

答案 3 :(得分:0)

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(Hi Karl!)发布了使用HttpHandler的帖子:

http://codebetter.com/blogs/karlseguin/archive/2006/06/12/146356.aspx

(不确定复制它的权限是什么,但如果你想写一个答案,你就得到了我的Upvote☺)

答案 4 :(得分:0)

如何添加一个新的UserControl子类来错误处理其渲染和加载方法(以便它们可以隐藏),然后继承用户控件的那个?

答案 5 :(得分:0)

我不确定我是否理解your response ..您是如何加载控件并将其添加到控件集中的?

这就是“更新”部分中添加的内容的全部内容。您可以随时随地灵活地使用 SafeLoader

我不确定您为什么觉得自己无法访问/控制Html? SafeLoader的目标是你不关心 html是什么,你只需尝试“输出”控件(在“bubble”中)并确定它是否在当前状态下加载OK。 / p>

如果确实如此(即返回html),那么你可以用它做你喜欢的事,输出html,将控件添加到控件集合中,等等!

如果没有,那么再一次,你可以做你喜欢的事情,渲染错误信息,抛出一个自定义异常..选择是你的!

我希望这有助于为你澄清一些事情,如果没有,那么请大声喊叫:)

答案 6 :(得分:0)

我使用了@ Keith的方法,但问题是控件在异常被抛出之前被渲染,可能导致打开HTML标记。如果处于调试模式,我也会在Control中呈现异常信息。

  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));
     }
  }