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

时间:2022-01-26 03:36:36

I'm dynamically loading user controls adding them to the Controls collection of the web form.

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

I'd like to hide user controls if they cause a unhandled exception while rendering.

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

So, I tried hooking to the Error event of each UserControl but it seems that this event never fires for the UserControls as it does for Page class.

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

Did some googling around and it doesn't seem promising. Any ideas here?

做了一些谷歌搜索,似乎并不乐观。这里有什么想法?

7 个解决方案

#1


12  

mmilic, following on from your response to my previous idea..

mmilic,继续你对我之前的想法的回应..

No additional logic required! That's the point, your doing nothing to the classes in question, just wrapping them in some instantiation bubble-wrap! :)

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

OK, I was going to just bullet point but I wanted to see this work for myself, so I cobbled together some very rough code but the concept is there and it seems to work.

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

APOLOGIES FOR THE LONG POST

长篇大论

The SafeLoader

This will basically be the "bubble" I mentioned.. It will get the controls HTML, catching any errors that occur during Rendering.

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

And Some Controls..

Ok I just mocked together two controls here, one will throw the other will render junk. Point here, I don't give a crap. These will be replaced with your custom controls..

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

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

The Page

OK, so lets look at the "test" page.. Here I simply instantiate the controls, grab their html and output it, I will follow with thoughts on designer support etc..

好的,让我们看看“测试”页面。在这里,我只是实例化控件,抓取他们的HTML并输出它,我将跟随设计师支持的想法等。

Page Code-Behind

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

Thoughts

OK, I know what you are thinking, "these controls are instantiated programatically, what about designer support? I spent freaking hours getting these controls nice for the designer, now you're messing with my mojo".

好吧,我知道你在想什么,“这些控件是以编程方式实例化的,设计师支持怎么样?我花了几个小时让这些控件对设计师很好,现在你正在弄乱我的魔力”。

OK, so I havent really tested this yet (probably will do in a min!) but the idea here is to override the CreateChildControls method for the page, and take the instance of each control added on the form and run it through the SafeLoader. If the code passes, you can add it to the Controls collection as normal, if not, then you can create erroneous literals or something, up to you my friend.

好的,所以我还没有真正测试过这个(可能会在一分钟内完成!)但这里的想法是覆盖页面的CreateChildControls方法,并获取在表单上添加的每个控件的实例并通过SafeLoader运行它。如果代码通过,您可以正常添加到Controls集合中,如果没有,那么您可以创建错误的文字或其他内容,由我的朋友决定。

Finally..

Again, sorry for the long post, but I wanted to get the code here so we can discuss this :) I hope this helps demonstrate my idea :)

再次,抱歉长篇文章,但我想在这里得到代码所以我们可以讨论这个:)我希望这有助于证明我的想法:)

Update

Tested by chucking a control in on the designer and overriding the CreateChildControls method with this, works fine, may need some clean up to make things better looking, but I'll leave that to you ;)

通过在设计器上放置一个控件并使用它来覆盖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>");
        }
    }
}

Enjoy!

#2


4  

This is an interesting problem.. I am still pretty fresh when it comes to custom controls etc, but here are my thoughts (feel free to comment/correct people!).. (I am kinda thinking/writing out loud here!)

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

  • If an error occurs during rendering, in some cases, would it not be too late? (since some of the controls HTML may have already been sent to the Writer and output).
  • 如果在渲染过程中发生错误,在某些情况下,是否会为时已晚? (因为某些控件HTML可能已经被发送到Writer并输出)。

  • Therefore, would it not be best to wrap the user control's Render method, but rather than passing it the reference to the "Live" HtmlTextWriter, you pass your own, trap any Exceptions raised in this little safety "bubble", if all goes well, you then pass your resultant HTML to the actual HtmlTextWriter?
  • 因此,最好不要将用户控件的Render方法包装起来,而是将其传递给“Live”HtmlTextWriter,而不是将其传递给你自己,如果一切顺利的话,陷入在这个小安全“泡沫”中引发的任何异常,然后将结果HTML传递给实际的HtmlTextWriter?

  • This logic could probably be slung to a generic wrapper class which you would use to dynamically load/render the controls at run time..
  • 这个逻辑可能会被挂到一个通用的包装类中,你可以在运行时动态加载/呈现控件。

  • If any errors do occur, you have all the information you need at your disposal! (i.e control references etc).
  • 如果确实发生任何错误,您可以获得所需的所有信息! (即控制参考等)。

Just my thoughts, flame away! :D ;)

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

#3


1  

Depending on where your errors are occurring you can do something like...

根据您的错误发生地点,您可以执行以下操作:

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*/ }
    }
}

Then inherit SilentErrorControl instead of UserControl.

然后继承SilentErrorControl而不是UserControl。

#4


0  

Global.asax and Application_Error?

Global.asax和Application_Error?

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

Or the Page_Error Event on an individual Page only:

或仅限单个页面上的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();
}

Also, Karl Seguin (Hi Karl!) had a Post on using HttpHandler instead:

另外,Karl Seguin(Hi Karl!)在使用HttpHandler时有一个帖子:

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

(Not sure what the permission to reproduce it, but if you want to write up an answer, you got my Upvote ☺)

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

#5


0  

How about adding a new sub-class of UserControl that error-handles its render and load methods (so that they hide as you wish) and then inheriting from that for your user controls?

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

#6


0  

I am not sure I understand your response.. How are you loading your controls and adding them to your controls collection?

我不确定我理解你的回答..你如何加载你的控件并将它们添加到你的控件集合中?

That was the whole point of the bit added in the "Update" section.. You have the flexibility to use the SafeLoader wherever you please.

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

I am not sure why you feel you don't have access/control over the Html? The goal of the SafeLoader is that you dont care what the html is, you simply try and "output" the control (within the "bubble") and determine if it loads OK in its current state.

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

If it does (i.e. the html is returned) then you can do what you like with it, output the html, add the control to the controls collection, whatever!

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

If not, then again, you can do what you like, render an error message, throw a custom exception.. The choice is yours!

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

I hope this helps clarify things for you, if not, then please shout :)

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

#7


0  

I used @Keith's approach, but the problem is that the control is rendered up until the Exception is thrown, potentially resulting in open HTML tags. I'm also rendering the exception information in the Control if in Debug mode.

我使用了@Kith的方法,但问题是控件在异常被抛出之前被渲染,可能导致打开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));
     }
  }

#1


12  

mmilic, following on from your response to my previous idea..

mmilic,继续你对我之前的想法的回应..

No additional logic required! That's the point, your doing nothing to the classes in question, just wrapping them in some instantiation bubble-wrap! :)

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

OK, I was going to just bullet point but I wanted to see this work for myself, so I cobbled together some very rough code but the concept is there and it seems to work.

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

APOLOGIES FOR THE LONG POST

长篇大论

The SafeLoader

This will basically be the "bubble" I mentioned.. It will get the controls HTML, catching any errors that occur during Rendering.

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

And Some Controls..

Ok I just mocked together two controls here, one will throw the other will render junk. Point here, I don't give a crap. These will be replaced with your custom controls..

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

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

The Page

OK, so lets look at the "test" page.. Here I simply instantiate the controls, grab their html and output it, I will follow with thoughts on designer support etc..

好的,让我们看看“测试”页面。在这里,我只是实例化控件,抓取他们的HTML并输出它,我将跟随设计师支持的想法等。

Page Code-Behind

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

Thoughts

OK, I know what you are thinking, "these controls are instantiated programatically, what about designer support? I spent freaking hours getting these controls nice for the designer, now you're messing with my mojo".

好吧,我知道你在想什么,“这些控件是以编程方式实例化的,设计师支持怎么样?我花了几个小时让这些控件对设计师很好,现在你正在弄乱我的魔力”。

OK, so I havent really tested this yet (probably will do in a min!) but the idea here is to override the CreateChildControls method for the page, and take the instance of each control added on the form and run it through the SafeLoader. If the code passes, you can add it to the Controls collection as normal, if not, then you can create erroneous literals or something, up to you my friend.

好的,所以我还没有真正测试过这个(可能会在一分钟内完成!)但这里的想法是覆盖页面的CreateChildControls方法,并获取在表单上添加的每个控件的实例并通过SafeLoader运行它。如果代码通过,您可以正常添加到Controls集合中,如果没有,那么您可以创建错误的文字或其他内容,由我的朋友决定。

Finally..

Again, sorry for the long post, but I wanted to get the code here so we can discuss this :) I hope this helps demonstrate my idea :)

再次,抱歉长篇文章,但我想在这里得到代码所以我们可以讨论这个:)我希望这有助于证明我的想法:)

Update

Tested by chucking a control in on the designer and overriding the CreateChildControls method with this, works fine, may need some clean up to make things better looking, but I'll leave that to you ;)

通过在设计器上放置一个控件并使用它来覆盖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>");
        }
    }
}

Enjoy!

#2


4  

This is an interesting problem.. I am still pretty fresh when it comes to custom controls etc, but here are my thoughts (feel free to comment/correct people!).. (I am kinda thinking/writing out loud here!)

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

  • If an error occurs during rendering, in some cases, would it not be too late? (since some of the controls HTML may have already been sent to the Writer and output).
  • 如果在渲染过程中发生错误,在某些情况下,是否会为时已晚? (因为某些控件HTML可能已经被发送到Writer并输出)。

  • Therefore, would it not be best to wrap the user control's Render method, but rather than passing it the reference to the "Live" HtmlTextWriter, you pass your own, trap any Exceptions raised in this little safety "bubble", if all goes well, you then pass your resultant HTML to the actual HtmlTextWriter?
  • 因此,最好不要将用户控件的Render方法包装起来,而是将其传递给“Live”HtmlTextWriter,而不是将其传递给你自己,如果一切顺利的话,陷入在这个小安全“泡沫”中引发的任何异常,然后将结果HTML传递给实际的HtmlTextWriter?

  • This logic could probably be slung to a generic wrapper class which you would use to dynamically load/render the controls at run time..
  • 这个逻辑可能会被挂到一个通用的包装类中,你可以在运行时动态加载/呈现控件。

  • If any errors do occur, you have all the information you need at your disposal! (i.e control references etc).
  • 如果确实发生任何错误,您可以获得所需的所有信息! (即控制参考等)。

Just my thoughts, flame away! :D ;)

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

#3


1  

Depending on where your errors are occurring you can do something like...

根据您的错误发生地点,您可以执行以下操作:

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*/ }
    }
}

Then inherit SilentErrorControl instead of UserControl.

然后继承SilentErrorControl而不是UserControl。

#4


0  

Global.asax and Application_Error?

Global.asax和Application_Error?

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

Or the Page_Error Event on an individual Page only:

或仅限单个页面上的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();
}

Also, Karl Seguin (Hi Karl!) had a Post on using HttpHandler instead:

另外,Karl Seguin(Hi Karl!)在使用HttpHandler时有一个帖子:

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

(Not sure what the permission to reproduce it, but if you want to write up an answer, you got my Upvote ☺)

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

#5


0  

How about adding a new sub-class of UserControl that error-handles its render and load methods (so that they hide as you wish) and then inheriting from that for your user controls?

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

#6


0  

I am not sure I understand your response.. How are you loading your controls and adding them to your controls collection?

我不确定我理解你的回答..你如何加载你的控件并将它们添加到你的控件集合中?

That was the whole point of the bit added in the "Update" section.. You have the flexibility to use the SafeLoader wherever you please.

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

I am not sure why you feel you don't have access/control over the Html? The goal of the SafeLoader is that you dont care what the html is, you simply try and "output" the control (within the "bubble") and determine if it loads OK in its current state.

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

If it does (i.e. the html is returned) then you can do what you like with it, output the html, add the control to the controls collection, whatever!

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

If not, then again, you can do what you like, render an error message, throw a custom exception.. The choice is yours!

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

I hope this helps clarify things for you, if not, then please shout :)

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

#7


0  

I used @Keith's approach, but the problem is that the control is rendered up until the Exception is thrown, potentially resulting in open HTML tags. I'm also rendering the exception information in the Control if in Debug mode.

我使用了@Kith的方法,但问题是控件在异常被抛出之前被渲染,可能导致打开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));
     }
  }