背景
為什么語言引入了異常
一直沒有思考過這個問題,但是異常確實讓我的編程生活更快樂,今天早上似乎找到了這個問題的答案:exception之於call stack就像break和continue之於while或for、就像return之於method,總結為一句話:異常只是一種返回機制。
為什么異常讓程序更簡潔
代碼里只有正常的處理邏輯。
1 /// <summary> 2 /// 創建。 3 /// </summary> 4 public ActionResult Create(TAggregateRoot item) 5 { 6 this.CurrentCommandService.Execute(new TCreateCommand 7 { 8 Aggregate = item 9 }); 10 11 return this.NewtonsoftJson(new 12 { 13 success = true, 14 items = this.GetById(item.Id) 15 }); 16 } 17 18 /// <summary> 19 /// 修改。 20 /// </summary> 21 public ActionResult Update(TAggregateRoot item) 22 { 23 this.CurrentCommandService.Execute(new TUpdateCommand 24 { 25 Aggregate = item 26 }); 27 28 return this.NewtonsoftJson(new 29 { 30 success = true, 31 items = this.GetById(item.Id) 32 }); 33 }
我的程序代碼基本上也是CQRS的,凡是以寫為目的的,都是用void進行聲明。
異常的處理
異常有五種處理思路,如下圖所示:
關於這五種處理思路的概要介紹可以參考這篇文章:http://www.cnblogs.com/happyframework/archive/2013/04/09/3010082.html。
今天的主要目的是介紹“邊界異常處理”。
邊界異常處理
當異常到達邊界,毫無疑問我們必須進行處理。總體來說,到達邊界的異常分為兩大類:我們有意拋出的異常和未處理異常,針對這兩種異常我們需要不同的處理思路,如:
- 有意拋出的異常:不希望寫入日志,希望顯示到UI。
- 未處理的異常:希望寫入日志,不希望直接顯示到UI,希望定制這種異常的顯示信息。
一個簡單的邊界異常處理框架
結構
下圖的友好異常就是我們有意拋出的異常或我們能意識到的異常。
幾個核心類型
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 namespace Happy.Web.Mvc.ExceptionHanding 9 { 10 /// <summary> 11 /// 異常信息提供者接口,如果你希望為異常返回更多的信息,可以實現該接口。 12 /// </summary> 13 public interface IExceptionInformationProvider 14 { 15 /// <summary> 16 /// 創建信息。 17 /// </summary> 18 object CreateInformation(Exception exception); 19 } 20 }
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.Web.Mvc.Newtonsoft; 10 11 namespace Happy.Web.Mvc.ExceptionHanding 12 { 13 /// <summary> 14 /// 處理應用程序未捕獲的異常。 15 /// </summary> 16 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] 17 public class WriteExceptionResultAttribute : FilterAttribute, IExceptionFilter 18 { 19 /// <inheritdoc /> 20 public void OnException(ExceptionContext filterContext) 21 { 22 var exception = filterContext.Exception; 23 24 this.LogException(exception); 25 26 filterContext.Result = ExceptionInformationProviderRegistry.CreateErrorResult(exception); 27 28 filterContext.ExceptionHandled = true; 29 } 30 31 private void LogException(Exception exception) 32 { 33 if (FriendlyExceptionRegistry.Contains(exception.GetType())) 34 { 35 return; 36 } 37 38 LogManager.GetCurrentClassLogger().Error(exception); 39 } 40 } 41 }
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 using System.Data; 8 9 using Happy.ExtensionMethod; 10 using Happy.DesignByContract; 11 using Happy.Web.Mvc.Newtonsoft; 12 13 namespace Happy.Web.Mvc.ExceptionHanding 14 { 15 /// <summary> 16 /// 異常信息提供者注冊處。 17 /// </summary> 18 public static class ExceptionInformationProviderRegistry 19 { 20 private static readonly Dictionary<Type, IExceptionInformationProvider> _providers 21 = new Dictionary<Type, IExceptionInformationProvider>(); 22 23 static ExceptionInformationProviderRegistry() 24 { 25 Register<OptimisticConcurrencyException>(new Internal.OptimisticConcurrencyExceptionInformationProvider()); 26 } 27 28 /// <summary> 29 /// 注冊提供者。 30 /// </summary> 31 public static void Register<TException>(IExceptionInformationProvider provider) 32 where TException : Exception 33 { 34 Register(typeof(TException), provider); 35 } 36 37 /// <summary> 38 /// 注冊提供者。 39 /// </summary> 40 public static void Register(Type exceptionType, IExceptionInformationProvider provider) 41 { 42 exceptionType.MustNotNull("exceptionType"); 43 provider.MustNotNull("provider"); 44 45 _providers[exceptionType] = provider; 46 } 47 48 internal static ActionResult CreateErrorResult(Exception exception) 49 { 50 exception.MustNotNull("exception"); 51 52 var exceptionType = exception.GetType(); 53 54 var information = CreateDefaultInformation(exception); 55 56 if (_providers.ContainsKey(exceptionType)) 57 { 58 var extInformation = _providers[exceptionType].CreateInformation(exception); 59 60 foreach (var item in extInformation.ToDictionary()) 61 { 62 information[item.Key] = item.Value; 63 } 64 } 65 66 return new NewtonsoftJsonResult 67 { 68 Data = information 69 }; 70 } 71 72 private static Dictionary<string, object> CreateDefaultInformation(Exception exception) 73 { 74 return new Dictionary<string, object> 75 { 76 { "success", false }, 77 { "exception", exception.GetType().Name }, 78 { "message",exception.Message } 79 }; 80 } 81 } 82 }
為樂觀並發異常自定義返回消息
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Data; 7 using System.Web.Mvc; 8 9 using Happy.Web.Mvc.Newtonsoft; 10 11 namespace Happy.Web.Mvc.ExceptionHanding.Internal 12 { 13 internal sealed class OptimisticConcurrencyExceptionInformationProvider : ExceptionInformationProvider<OptimisticConcurrencyException> 14 { 15 protected override object CreateResult(OptimisticConcurrencyException exception) 16 { 17 return new 18 { 19 message = Messages.Error_OptimisticConcurrencyExceptionMessage 20 }; 21 } 22 } 23 }
備注
像微軟的異常處理框架都是一個非常好的東西。
合理的利用和使用異常會讓程序的結構更加簡潔,這些概念只有真正使用了才能深刻的明白。