探讨对Web控件的异常处理2012-01-10 博客园 简单生活在使用ASP.NET时,常常使用Page的错误事件Error进行错误捕捉和处理。这种方式可以集中处理所有异常,这种方式有利有弊。集中处理的好处就不用啰嗦了,这里只说明一下这种方式的局限,就是当页面中的某个控件发生异常之后,整个页面执行都会中断,然后处理异常,这样一来,页面就无法显示。在实际开发中,常常有这样的需求,即页面是由多个相对独立的控件组成,其中一个控件的错误不能影响到其它控件的正常显示。这就需要在控件内部捕捉错误,并自行处理错误,然而控件基类并没有提供这样的错误捕捉功能。如何用简单有效方法来实现呢?其实我们可以实现一个基类,并把所有在控件生命期中会调用到的方法都封装起来,这样只要继承这个控件,就可以方便地实现在控件内部自行捕捉错误的功能。请看下面的代码:
1public abstract class AbstractControl: Control 2{ 3 /**//// <summary> 4 /// 异常栈 5 /// </summary> 6 public Stack Exceptions 7 { 8 get 9 {10 if (exceptions == null)11 {12 exceptions = new Stack();13 }14 return exceptions;15 }16 }1718 protected override void CreateChildControls()19 {20 try21 {22 CreateChildControlsByCatchedException();23 }24 catch (HttpUnhandledException)25 {26 throw;27 }28 catch (Exception ex)29 {30 Exceptions.Push(ex);31 }32 }3334 /**//// <summary>35 /// 创建子控件(已进行异常捕捉处理)36 /// </summary>37 protected virtual void CreateChildControlsByCatchedException()38 {39 }4041 /**//// <summary>42 ///43 /// </summary>44 /// <param name="e"></param>45 protected override void OnPreRender(EventArgs e)46 {47 try48 {49 OnPreRenderByCatchedException(e);50 }51 catch (HttpUnhandledException)52 {53 throw;54 }55 catch (Exception ex)56 {57 Exceptions.Push(ex);58 }59 }6061 /**//// <summary>62 /// 呈现前事件(已进行错误捕捉处理)63 /// </summary>64 /// <param name="e"></param>65 protected virtual void OnPreRenderByCatchedException(EventArgs e)66 {67 base.OnPreRender (e);68 }69 70 /**//// <summary>71 /// 设计时的呈现前事件72 /// </summary>73 /// <param name="e"></param>74 protected virtual void DesigningOnPreRenderByCatchedException(EventArgs e)75 {76 }7778 /**//// <summary>79 /// 呈现80 /// </summary>81 /// <param name="writer"></param>82 protected override void Render(HtmlTextWriter writer)83 {84 if (Exceptions.Count > 0)85 {86 while (Exceptions.Count > 0 )87 {88 Exception ex = (Exception) Exceptions.Pop();89 RenderException(writer, ex);90 }91 return;92 }93 94 try95 {96 RenderByCatchedException(writer);97 }98 catch (HttpUnhandledException)99 {100 throw;101 }102 catch (Exception ex)103 {104 RenderException(writer, ex);105 }106 }107108 /**//// <summary>109 /// 呈现(已进行错误捕捉处理)110 /// </summary>111 /// <param name="writer"></param>112 protected virtual void RenderByCatchedException(HtmlTextWriter writer)113 {114 base.Render (writer);115 }116117 /**//// <summary>118 /// 呈现异常119 /// </summary>120 /// <param name="writer"></param>121 /// <param name="ex"></param>122 private void RenderException(HtmlTextWriter writer, Exception ex)123 { 124 writer.AddAttribute(HtmlTextWriterAttribute.Title, BuildExceptionInfomation(ex));125 writer.AddStyleAttribute("font-weight", "700");126 writer.AddStyleAttribute("color", "#f00");127 writer.AddStyleAttribute("border", "1px solid #ddd");128 writer.AddStyleAttribute("cursor", "pointer");129 writer.AddStyleAttribute("padding", "0px 3px 0px 3px");130 writer.AddStyleAttribute("background-color", "#ffe");131 writer.RenderBeginTag(HtmlTextWriterTag.Span);132 writer.Write("!");133 writer.RenderEndTag();134 }135136 /**//// <summary>137 /// 生成异常信息138 /// </summary>139 /// <param name="ex"></param>140 /// <returns></returns>141 private string BuildExceptionInfomation(Exception ex)142 {143 StringBuilder sb = new StringBuilder();144 sb.Append(ex.Message);145 sb.Append(Environment.NewLine);146 sb.Append(ex.GetType().FullName);147 sb.Append(Environment.NewLine);148 sb.Append(ex.StackTrace);149 return sb.ToString();150 }151 152 /**//// <summary>153 /// 中断程序的执行154 /// </summary>155 /// <param name="ex"></param>156 protected virtual void Interrupt(Exception ex)157 {158 throw new HttpUnhandledException(ex.Message, ex);159 }160}161上面的代码只重载了OnPreRender、Render和CreateChildControls三个方法,实际上还有OnInit、OnLoad等,可以视实际需要而重载,这样重载之后,所有错误都被捕捉,并存放在错误栈中,并在呈现时将错误以某种格式呈现在界面上。注意,继承AbstractControl基类的控件应重载如RenderByCatchedException之类的方法。如果某些错误不希望被捕捉,而是直接抛出到页面上,这时候还可以调用Interrupt方法来将错误直接抛出到页面上,并中断整个页面的执行。