探讨对Web控件的异常处理
在使用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 }
17
18 protected override void CreateChildControls()
19 {
20 try
21 {
22 CreateChildControlsByCatchedException();
23 }
24 catch (HttpUnhandledException)
25 {
26 throw;
27 }
28 catch (Exception ex)
29 408_436_Open_Image.style.display='inline'; Codehighlighter1_408_436_Open_Text.style.display='inline';" src="/Images/OutliningIndicators/ContractedSubBlock.gif" align=top> {
30 Exceptions.Push(ex);
31 }
32 }
33
34 /// <summary>
35 /// 创建子控件(已进行异常捕捉处理)
36 /// </summary>
37 protected virtual void CreateChildControlsByCatchedException()
38 {
39 }
40
41 /// <summary>
42 ///
43 /// </summary>
44 /// <param name="e"></param>
45 protected override void OnPreRender(EventArgs e)
46 {
47 try
48 {
49 OnPreRenderByCatchedException(e);
50 }
51 catch (HttpUnhandledException)
52 {
53 throw;
54 }
55 catch (Exception ex)
56 {
57 Exceptions.Push(ex);
58 }
59 }
60
61 /// <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 }
77
78 /// <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 try
95 {
96 RenderByCatchedException(writer);
97 }
98 catch (HttpUnhandledException)
99 {
100 throw;
101 }
102 catch (Exception ex)
103 {
104 RenderException(writer, ex);
105 }
106 }
107
108 /// <summary>
109 /// 呈现(已进行错误捕捉处理)
110 /// </summary>
111 /// <param name="writer"></param>
112 protected virtual void RenderByCatchedException(HtmlTextWriter writer)
113 {
114 base.Render (writer);
115 }
116
117 /// <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 }
135
136 /// <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
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 }
17
18 protected override void CreateChildControls()
19 {
20 try
21 {
22 CreateChildControlsByCatchedException();
23 }
24 catch (HttpUnhandledException)
25 {
26 throw;
27 }
28 catch (Exception ex)
29 408_436_Open_Image.style.display='inline'; Codehighlighter1_408_436_Open_Text.style.display='inline';" src="/Images/OutliningIndicators/ContractedSubBlock.gif" align=top> {
30 Exceptions.Push(ex);
31 }
32 }
33
34 /// <summary>
35 /// 创建子控件(已进行异常捕捉处理)
36 /// </summary>
37 protected virtual void CreateChildControlsByCatchedException()
38 {
39 }
40
41 /// <summary>
42 ///
43 /// </summary>
44 /// <param name="e"></param>
45 protected override void OnPreRender(EventArgs e)
46 {
47 try
48 {
49 OnPreRenderByCatchedException(e);
50 }
51 catch (HttpUnhandledException)
52 {
53 throw;
54 }
55 catch (Exception ex)
56 {
57 Exceptions.Push(ex);
58 }
59 }
60
61 /// <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 }
77
78 /// <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 try
95 {
96 RenderByCatchedException(writer);
97 }
98 catch (HttpUnhandledException)
99 {
100 throw;
101 }
102 catch (Exception ex)
103 {
104 RenderException(writer, ex);
105 }
106 }
107
108 /// <summary>
109 /// 呈现(已进行错误捕捉处理)
110 /// </summary>
111 /// <param name="writer"></param>
112 protected virtual void RenderByCatchedException(HtmlTextWriter writer)
113 {
114 base.Render (writer);
115 }
116
117 /// <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 }
135
136 /// <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方法来将错误直接抛出到页面上,并中断整个页面的执行。