代码之家  ›  专栏  ›  技术社区  ›  markom

捕获ASP.NET用户控件中未处理的异常

  •  4
  • markom  · 技术社区  · 16 年前

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

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

    所以,我尝试挂接到每个用户控件的错误事件,但似乎这个事件对于用户控件永远不会像对页类那样触发。

    做了一些谷歌搜索,但似乎不太可能。有什么想法吗?

    7 回复  |  直到 12 年前
        1
  •  12
  •   Community    7 年前

    mmilic,从开始 your response 对我 previous idea

    不需要额外的逻辑!这就是重点,您对所讨论的类不做任何事情,只需将它们包装在一些实例化气泡包装中!:)

    好吧,我本来想说重点,但我想亲眼看看,所以我拼凑了一些 非常 粗略的代码,但概念是存在的,它似乎是工作的。

    为长篇文章道歉

    保险柜

    这基本上就是我提到的“气泡”。它将获取控件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方法,并获取表单上添加的每个控件的实例,并通过安全加载程序运行它。如果代码通过,您可以正常地将其添加到控件集合中,如果没有,那么您可以创建错误的文本或其他内容,这取决于您本人的朋友。

    最后…

    再次,对这篇长文章表示抱歉,但我想在这里获取代码,以便我们讨论这个问题:) 我希望这有助于证明我的想法:)

    更新

    通过将控件放入设计器中并用此重写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>");
            }
        }
    }
    

    享受!

        2
  •  4
  •   Rob Cooper    16 年前

    这是一个有趣的问题。当涉及到自定义控件等时,我还是很新鲜的,但这里是我的想法(请随意评论/纠正他人!)…(我在这里有点想/写得很大声!)

    • 如果在渲染过程中发生错误,在某些情况下,是否不会太迟?(由于某些控件HTML可能已发送到编写器和输出)。
    • 因此,是否最好包装用户控件的呈现方法,而不是将引用传递给“live”htmltextwriter,而是传递自己的,捕获在这个小安全“bubble”中引发的任何异常,如果一切正常,则将生成的HTML传递给实际的htmltextwriter?
    • 此逻辑可能挂起到一个通用包装类,您将使用该类在运行时动态加载/呈现控件。
    • 如果确实发生了任何错误,您可以随时掌握所需的所有信息!(即控制参考等)。

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

        3
  •  1
  •   Keith    16 年前

    根据错误发生的位置,您可以执行以下操作…

    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。

        4
  •  0
  •   Michael Stum    16 年前

    global.asax和应用程序错误?

    http://www.15seconds.com/issue/030102.htm

    或者只在单个页面上显示页面错误事件:

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

    还有,卡尔·塞古(你好,卡尔!)改为使用httphandler:

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

    (不知道允许复制什么,但是如果你想写一个答案,你得到了我的赞成票)

        5
  •  0
  •   Keith    16 年前

    如何添加一个新的UserControl子类,该类错误处理它的呈现和加载方法(以便它们根据您的需要隐藏),然后为您的用户控件继承这些方法?

        6
  •  0
  •   Community    7 年前

    我不确定我明白 your response …如何加载控件并将其添加到控件集合中?

    这就是在“更新”部分中添加的位的全部内容。您可以灵活地使用 保险柜 你想去哪儿都行。

    我不知道为什么你觉得你没有对HTML的访问/控制权?安全加载器的目标是你不 照顾 HTML是什么,您只需尝试“输出”控件(在“气泡”中),并确定它在当前状态下是否加载正常。

    如果是这样(即返回HTML),那么您可以使用它做您喜欢的事情,输出HTML,将控件添加到控件集合中,无论什么!

    如果不是,那么您可以做您喜欢做的事情,呈现一条错误消息,抛出一个自定义异常。选择权属于你!

    我希望这有助于你澄清问题,如果没有,请大喊:)

        7
  •  0
  •   ThisGuy    12 年前

    我使用了@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));
         }
      }