探讨对Web控件的异常处理
在使用ASP.NET时,常常使用Page的错误事件Error进行错误捕捉和处理。这种方式可以集中处理所有异常,这种方式有利有弊。集中处理的好处就不用啰嗦了,这里只说明一下这种方式的局限,就是当页面中的某个控件发生异常之后,整个页面执行都会中断,然后处理异常,这样一来,页面就无法显示。
在实际开发中,常常有这样的需求,即页面是由多个相对独立的控件组成,其中一个控件的错误不能影响到其它控件的正常显示。这就需要在控件内部捕捉错误,并自行处理错误,然而控件基类并没有提供这样的错误捕捉功能。如何用简单有效方法来实现呢?
其实我们可以实现一个基类,并把所有在控件生命期中会调用到的方法都封装起来,这样只要继承这个控件,就可以方便地实现在控件内部自行捕捉错误的功能。请看下面的代码:
1
public 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

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

上面的代码只重载了OnPreRender、Render和CreateChildControls三个方法,实际上还有OnInit、OnLoad等,可以视实际需要而重载,这样重载之后,所有错误都被捕捉,并存放在错误栈中,并在呈现时将错误以某种格式呈现在界面上。注意,继承AbstractControl基类的控件应重载如RenderByCatchedException之类的方法。
如果某些错误不希望被捕捉,而是直接抛出到页面上,这时候还可以调用Interrupt方法来将错误直接抛出到页面上,并中断整个页面的执行。