背景
架构之处必须考虑:如何处理异常?如何定义自己的异常体系?本文为了强化这个概念而写。
异常处理的两条“黄金定律”
自己抄袭的两条规律:
- 异常不能穿过“边界类”。
- 异常不能在没有恢复的情况下“吞掉”。
我们会将异常分为两类:“需要恢复”和“不需要恢复”,“需要恢复”的异常如果到达了边界类,就说明系统有BUG了,这类异常需要记录到日志。“不需要恢复”的异常需要进一步分为:“我们不能恢复”和“我们不期望恢复”,如果这类异常到达边界类,“我们不能恢复“的异常同样需要记录到日志,“我们不期望恢复”的异常则直接将异常信息显示给界面。一般采用AOP处理边界异常。
示例
AOP
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using System.Web.Mvc;
7
8 using Common.Logging;
9 using Happy.ExceptionHanding;
10 using Happy.Web.Mvc.Newtonsoft;
11
12 namespace Happy.Web.Mvc.ExceptionHanding
13 {
14 /// <summary>
15 /// 处理应用程序未捕获的异常。
16 /// </summary>
17 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
18 public class WriteExceptionResultAttribute : FilterAttribute, IExceptionFilter
19 {
20 /// <inheritdoc />
21 public void OnException(ExceptionContext filterContext)
22 {
23 var exception = filterContext.Exception;
24 if (!FriendlyExceptionRegistry.IsFriendly(exception.GetType()))
25 {
26 LogManager.GetCurrentClassLogger().Error(exception);
27 }
28 filterContext.Result = CreateErrorResult(exception);
29 filterContext.ExceptionHandled = true;
30 }
31
32 private static ActionResult CreateErrorResult(Exception exception)
33 {
34 var information = ExceptionInformationProviderRegistry.CreateInformation(exception);
35
36 return new NewtonsoftJsonResult
37 {
38 Data = information
39 };
40 }
41 }
42 }
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 using Happy.ExtentionMethods;
8
9 namespace Happy.ExceptionHanding
10 {
11 /// <summary>
12 /// 异常信息提供者注册处。
13 /// </summary>
14 public static class ExceptionInformationProviderRegistry
15 {
16 private static readonly Dictionary<Type, IExceptionInformationProvider> _providers
17 = new Dictionary<Type, IExceptionInformationProvider>();
18
19 /// <summary>
20 /// 注册提供者。
21 /// </summary>
22 public static void Register<TException>(IExceptionInformationProvider provider)
23 where TException : Exception
24 {
25 Register(typeof(TException), provider);
26 }
27
28 /// <summary>
29 /// 注册提供者。
30 /// </summary>
31 public static void Register(Type exceptionType, IExceptionInformationProvider provider)
32 {
33 exceptionType.MustNotNull("exceptionType");
34 provider.MustNotNull("provider");
35
36 _providers[exceptionType] = provider;
37 }
38
39 public static Dictionary<string, object> CreateInformation(Exception exception)
40 {
41 exception.MustNotNull("exception");
42
43 var exceptionType = exception.GetType();
44
45 var information = CreateDefaultInformation(exception);
46
47 if (_providers.ContainsKey(exceptionType))
48 {
49 var extInformation = _providers[exceptionType].CreateInformation(exception);
50
51 foreach (var item in extInformation.ToDictionary())
52 {
53 information[item.Key] = item.Value;
54 }
55 }
56 else
57 {
58 if (FriendlyExceptionRegistry.IsFriendly(exception.GetType()))
59 {
60 information["exception"] = Resource.Messages.Msg_DefaultExceptionMessage;
61 }
62 }
63
64 return information;
65 }
66
67 private static Dictionary<string, object> CreateDefaultInformation(Exception exception)
68 {
69 return new Dictionary<string, object>
70 {
71 { "success", false },
72 { "exception", exception.GetType().Name },
73 { "message",exception.Message }
74 };
75 }
76 }
77 }
备注
放弃继续玩 GO 的一个原因就是:GO 的异常处理太不爽了,或者是我自己的原因,不够 OPEN。
|