【转】异常处理模块

一、前言

  异常处理是每个系统必不可少的一个重要部分,它可以让我们的程序在发生错误时友好地提示、记录错误信息,更重要的是不破坏正常的数据和影响系统运行。异常处理应该是一个横切点,所谓横切点就是各个部分都会使用到它,无论是分层中的哪一个层,还是具体的哪个业务逻辑模块,所关注的都是一样的。所以,横切关注点我们会统一在一个地方进行处理。无论是MVC还是WebForm都提供了这样实现,让我们可以集中处理异常。

  在MVC中,在FilterConfig中,已经默认帮我们注册了一个HandleErrorAttribute,这是一个过滤器,它继承了FilterAttribute类和实现了IExceptionFilter接口,关于过滤器的执行过程,可以看我的上一篇文章。说到异常处理,马上就会联想到500错误页面、记录日志等,HandleErrorAttribute可以轻松的定制错误页,默认就是Error页面;而记录日志我们也只需要继承它,并替换它注册到GlobalFilterCollection即可。关于HandleErrorAttribute很多人都知道怎么使用了,这里就不做介绍了。

  ok,开始进入主题!在MVC中处理异常,相信开始很多人都是继承HandleErrorAttribute,然后重写OnException方法,加入自己的逻辑,例如将异常信息写入日志文件等。当然,这并没有任何不妥,但良好的设计应该是场景驱动的,是动态和可配置的。例如,在场景一种,我们希望ExceptionA显示错误页面A,而在场景二中,我们希望它显示的是错误页面B,这里的场景可能是跨项目了,也可能是在同一个系统的不同模块。另外,异常也可能是分级别的,例如ExceptionA发生时,我们只需要简单的恢复状态,程序可以继续运行,ExceptionB发生时,我们希望将它记录到文件或者系统日志,而ExceptionC发生时,是个较严重的错误,我们希望程序发生邮件或者短信通知。简单地说,不同的场景有不同的需求,而我们的程序需要更好的面对变化。当然,继承HandleErrorAttribute也完全可以实现上面所说的,只不过这里我不打算去扩展它,而是重新编写一个模块,并且可以与原有的HandleErrorAttribute共同使用。

二、设计及实现

2.1 定义配置信息

  从上面已经可以知道我们要做的事了,针对不同的异常,我们希望可以配置它的处理程序,错误页等。如下一个配置:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<!--自定义异常配置-->

<settingException>

  <exceptions>

    <!--add优先级高于group-->

    <add exception="Exceptions.PasswordErrorException"

         view ="PasswordErrorView"

         handler="ExceptionHandlers.PasswordErrorExceptionHandler"/>

    <groups>

      <!--group可以配置一种异常的view和handler-->

      <group view="EmptyErrorView" handler="ExceptionHandlers.EmptyExceptionHandler">

        <add exception="Exceptions.UserNameEmptyException"/>

        <add exception="Exceptions.EmailEmptyException"/>

      </group>       

    </groups>

  </exceptions>

</settingException>

  其中,add 节点用于增加具体的异常,它的 exception 属性是必须的,而view表示错误页,handler表示具体处理程序,如果view和handler都没有,异常将交给默认的HandleErrorAttribute处理。而group节点用于分组,例如上面的UserNameEmptyException和EmailEmptyException对应同一个处理程序和视图。

  程序会反射读取这个配置信息,并创建相应的对象。我们把这个配置文件放到Web.config中,保证它可以随时改随时生效。

2.2 异常信息包装对象

  这里我们定义一个实体对象,对应上面的节点。如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class ExceptionConfig

{

    /// <summary>

    /// 视图

    /// </summary>

    public string View{get;set;}

    /// <summary>

    /// 异常对象

    /// </summary>

    public Exception Exception{get;set;}

    /// <summary>

    /// 异常处理程序

    /// </summary>

    public IExceptionHandler Handler{get;set;}

}

2.3 定义Handler接口

  上面我们说到,不同异常可能需要不同处理方式。这里我们设计一个接口如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

public interface IExceptionHandler

{

    /// <summary>

    /// 异常是否处理完成

    /// </summary>

    bool HasHandled{get;set;}

    /// <summary>

    /// 处理异常

    /// </summary>

    /// <param name="ex"></param>

    void Handle(Exception ex);

}

  各种异常处理程序只要实现该接口即可。

2.3 实现IExceptionFilter

  这是必须的。如下,实现IExceptionFilter接口,SettingExceptionProvider会根据异常对象类型从配置信息(缓存)获取包装对象。


1

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

public class SettingHandleErrorFilter : IExceptionFilter

{

    public void OnException(ExceptionContext filterContext)

    {

        if(filterContext == null)

        {

            throw new ArgumentNullException("filterContext");

        }

        ExceptionConfig config = SettingExceptionProvider.Container[filterContext.Exception.GetType()];

        if(config == null)

        {

            return;

        }

        if(config.Handler != null)

        {

            //执行Handle方法               

            config.Handler.Handle(filterContext.Exception);

            if (config.Handler.HasHandled)

            {

                //异常已处理,不需要后续操作

                filterContext.ExceptionHandled = true;

                return;

            }

        }           

        //否则,如果有定制页面,则显示

        if(!string.IsNullOrEmpty(config.View))

        {

            //这里还可以扩展成实现IView的视图

            ViewResult view = new ViewResult();

            view.ViewName = config.View;

            filterContext.Result = view;

            filterContext.ExceptionHandled = true;

            return;

        }

        //否则将异常继续传递

    }

}

2.4 读取配置文件,创建异常信息包装对象

  这部分代码比较多,事实上,你只要知道它是在读取web.config的自定义配置节点即可。SettingExceptionProvider用于提供容器对象。


1

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

public class SettingExceptionProvider

{

    public static Dictionary<Type, ExceptionConfig> Container =

        new Dictionary<Type, ExceptionConfig>();

    static SettingExceptionProvider()

    {

        InitContainer();

    }

    //读取配置信息,初始化容器

    private static void InitContainer()

    {

        var section = WebConfigurationManager.GetSection("settingException"as SettingExceptionSection;

        if(section == null)

        {

            return;

        }

        InitFromGroups(section.Exceptions.Groups);

        InitFromAddCollection(section.Exceptions.AddCollection);

    }

    private static void InitFromGroups(GroupCollection groups)

    {                     

        foreach (var group in groups.Cast<GroupElement>())

        {  

            ExceptionConfig config = new ExceptionConfig();

            config.View = group.View;

            config.Handler = CreateHandler(group.Handler);

            foreach(var item in group.AddCollection.Cast<AddElement>())

            {

                Exception ex = CreateException(item.Exception);

                config.Exception = ex;

                Container[ex.GetType()] = config;

            }

        }

    }

    private static void InitFromAddCollection(AddCollection collection)

    {

        foreach(var item in collection.Cast<AddElement>())

        {

            ExceptionConfig config = new ExceptionConfig();

            config.View = item.View;

            config.Handler = CreateHandler(item.Handler);

            config.Exception = CreateException(item.Exception);

            Container[config.Exception.GetType()] = config;

        }

    }

    //根据完全限定名创建IExceptionHandler对象

    private static IExceptionHandler CreateHandler(string fullName)            

    {

        if(string.IsNullOrEmpty(fullName))

        {

            return null;

        }

        Type type = Type.GetType(fullName);

        return Activator.CreateInstance(type) as IExceptionHandler;

    }

    //根据完全限定名创建Exception对象

    private static Exception CreateException(string fullName)

    {

        if(string.IsNullOrEmpty(fullName))

        {

            return null;

        }

        Type type = Type.GetType(fullName);

        return Activator.CreateInstance(type) as Exception;

    }

}

  以下是各个配置节点的信息:

  settingExceptions节点:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

/// <summary>

/// settingExceptions节点

/// </summary>

public class SettingExceptionSection : ConfigurationSection

{

    [ConfigurationProperty("exceptions",IsRequired=true)]

    public ExceptionsElement Exceptions

    {

        get

        {

            return (ExceptionsElement)base["exceptions"];

        }

    }

}

  exceptions节点:


1

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

/// <summary>

/// exceptions节点

/// </summary>

public class ExceptionsElement : ConfigurationElement

{

    private static readonly ConfigurationProperty _addProperty =

        new ConfigurationProperty(""typeof(AddCollection), null, ConfigurationPropertyOptions.IsDefaultCollection);

    [ConfigurationProperty("", IsDefaultCollection = true)]

    public AddCollection AddCollection

    {

        get

        {

            return (AddCollection)base[_addProperty];

        }

    }

    [ConfigurationProperty("groups")]

    public GroupCollection Groups

    {

        get

        {

            return (GroupCollection)base["groups"];

        }

    }

}

  Group节点集:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/// <summary>

/// group节点集

/// </summary>

[ConfigurationCollection(typeof(GroupElement),AddItemName="group")]

public class GroupCollection : ConfigurationElementCollection

{      

    /*override*/

    protected override ConfigurationElement CreateNewElement()

    {

        return new GroupElement();

    }

    protected override object GetElementKey(ConfigurationElement element)

    {

        return element;

    }

}

  group节点:


1

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

/// <summary>

/// group节点

/// </summary>

public class GroupElement : ConfigurationElement

{

    private static readonly ConfigurationProperty _addProperty =

        new ConfigurationProperty(""typeof(AddCollection), null, ConfigurationPropertyOptions.IsDefaultCollection);

    [ConfigurationProperty("view")]

    public string View

    {

        get

        {

            return base["view"].ToString();

        }

    }

    [ConfigurationProperty("handler")]

    public string Handler

    {

        get

        {

            return base["handler"].ToString();

        }

    }

    [ConfigurationProperty("", IsDefaultCollection = true)]

    public AddCollection AddCollection

    {

        get

        {

            return (AddCollection)base[_addProperty];

        }

    }       

}

  add节点集:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/// <summary>

/// add节点集

/// </summary>   

public class AddCollection : ConfigurationElementCollection

{         

    /*override*/

    protected override ConfigurationElement CreateNewElement()

    {

        return new AddElement();

    }

    protected override object GetElementKey(ConfigurationElement element)

    {

        return element;

    }

}

  add节点:


1

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

/// <summary>

/// add节点

/// </summary>

public class AddElement : ConfigurationElement

{

    [ConfigurationProperty("view")]

    public string View

    {

        get

        {

            return base["view"as string;

        }

    }

    [ConfigurationProperty("handler")]

    public string Handler

    {

        get

        {

            return base["handler"as string;

        }

    }

    [ConfigurationProperty("exception", IsRequired = true)]

    public string Exception

    {

        get

        {

            return base["exception"as string;

        }

    }

}

三、测试

  ok,下面测试一下,首先要在FilterConfig的RegisterGlobalFilters方法中在,HandlerErrorAttribute前注册我们的过滤器:

  filters.Add(new SettingHandleErrorFilter())。

3.1 准备异常对象

   准备几个简单的异常对象:


1

2

3

public class PasswordErrorException : Exception{}

public class UserNameEmptyException : Exception{}

public class EmailEmptyException : Exception{}

3.2 准备Handler

  针对上面的异常,我们准备两个Handler,一个处理密码错误异常,一个处理空异常。这里没有实际处理代码,具体怎么处理,应该结合具体业务了。如:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public class PasswordErrorExceptionHandler : IExceptionHandler

{

    public bool HasHandled{get;set;}

    

    public void Handle(Exception ex)

    {

        //具体处理逻辑...

    }

}

public class EmptyExceptionHandler : IExceptionHandler

{

    public bool HasHandled { getset; }

    public void Handle(Exception ex)

    {

        //具体处理逻辑...

    }

}

3.3 抛出异常

  按照上面的配置,我们在Action中手动throw异常


1

2

3

4

5

6

7

8

9

10

11

12

public ActionResult Index()

{

    throw new PasswordErrorException();

}

public ActionResult Index2()

{

    throw new UserNameEmptyException();

}

public ActionResult Index3()

{

    throw new EmailEmptyException();

}

  可以看到,相应的Handler会被执行,浏览器也会出现我们配置的错误页面。

四、总结

  事实上这只是一个比较简单的例子,所以我称它为简单的模块,而是用框架、库之类的词。当然我们可以根据实际情况对它进行扩展和优化。微软企业库视乎也集成这样的模块,有兴趣的朋友可以了解一下。

时间: 2024-07-31 14:32:37

【转】异常处理模块的相关文章

NET MVC异常处理模块

一个简单的ASP.NET MVC异常处理模块 一.前言 异常处理是每个系统必不可少的一个重要部分,它可以让我们的程序在发生错误时友好地提示.记录错误信息,更重要的是不破坏正常的数据和影响系统运行.异常处理应该是一个横切点,所谓横切点就是各个部分都会使用到它,无论是分层中的哪一个层,还是具体的哪个业务逻辑模块,所关注的都是一样的.所以,横切关注点我们会统一在一个地方进行处理.无论是MVC还是WebForm都提供了这样实现,让我们可以集中处理异常. 在MVC中,在FilterConfig中,已经默认

解析大型.NET ERP系统 设计异常处理模块

异常处理模块是大型系统必备的一个组件,精心设计的异常处理模块可提高系统的健壮性.下面从我理解的角度,谈谈异常处理的方方面面.我的设计仅仅限定于Windows Forms,供参考. 1 定义异常类型 .NET 框架定义很多异常类型,ERP系统中根据实际的需要,我们再增加一些自定义的异常类型. 数据库访问异常:LLBL Gen Pro已经定义几种常见的异常类型,常见的异常类型及其作用简介. ORMConcurrencyException     并发异常,更新实体时实体已经被删除,删除时有约束无法删

?DRF?-----解析模块 异常处理模块 响应模块 序列化模块(重点)

接口复习 1.接口:url+请求参数+响应参数 Postman发送接口请求的工具 method: GET url: https://api.map.baidu.com/place/v2/search params: ak: 6E823f587c95f0148c19993539b99295 region: 上海 query: 肯德基 output: json restful接口规范 https://api.baidu.com/v1/books?ordering=-price&limit=3 get

跨平台信息获取小工具第三版本(增加了继承、多线程、异常处理模块、excel表格内容剔除空格)

# coding=utf-8 import threadingimport paramikoimport osimport timeimport xlrdimport xlwtimport openpyxl all_row = []threads = [] class read_excel(object): def __init__(self, num): #threading.Thread.__init__(self) #self.threadID = threadID self.num =

Web API系列(三) 异常处理

在上一篇教程中我为大家介绍了Web API中Filter的开发使用,其中讲到ExceptionFilter时留了一个坑:ExceptionFilter只能截获并处理Action执行过程中发生的异常,在Action执行过程之外如果出现异常,ExceptionFilter是无能为力的. 这些异常包括: 1.  Controller构造方法中出现的异常 2.  MessageHandlers中出现的异常 3.  路由过程中出现的异常 4.  Body在序列化/反序列化过程中出现的异常 由此可以看出,E

Java异常处理总结Exception\Error

Java异常处理总结Exception\Error 2012-12-28 08:17:17|  分类: JAVA |  标签:java  |举报|字号 订阅 Java异常处理总结 异常处理是程序设计中一个非常重要的方面,也是程序设计的一大难点,从C开始,你也许已经知道如何用if...else...来控制异常了,也许是自发的,然而这种控制异常痛苦,同一个异常或者错误如果多个地方出现,那么你每个地方都要做相同处理,感觉相当的麻烦! Java语言在设计的当初就考虑到这些问题,提出异常处理的框架的方案,

异常以及异常处理框架探析

概述 一般情况下,企业级应用都对应着复杂的业务逻辑,为了保证系统的健壮,必然需要面对各种系统业务异常和运行时异常. 不好的异常处理方式容易造成应用程序逻辑混乱,脆弱而难于管理.应用程序中充斥着零散的异常处理代码,使程序代码晦涩难懂.可读性差,并且难于维护. 一个好的异常处理框架能为应用程序的异常处理提供统一的处理视图,把异常处理从程序正常运行逻辑分离出来,以至于提供更加结构化以及可读性的程序架构.另外,一个好的异常处理框架具备可扩展性,很容易根据具体的异常处理需求,扩展出特定的异常处理逻辑. 另

PHP错误和异常处理

PHP 5 添加了类似于其它语言的异常处理模块.在 PHP 代码中所产生的异常可被 throw 语句抛出并被 catch 语句捕获. 需要进行异常处理的代码都必须放入 try 代码块内,以便捕获可能存在的异常.每一个 try 至少要有一个与之对应的 catch. 使用多个 catch 可以捕获不同的类所产生的异常.当 try 代码块不再抛出异常或者找不到 catch 能匹配所抛出的异常时,PHP 代码就会在跳转到最后一个 catch 的后面继续执行.当然,PHP 允许在 catch 代码块内再次

PLSQL_Oracle Exception异常分类、异常抛出、异常处理、异常传播(概念)

2014-06-03 BaoXinjian 一.摘要 在PLSQL程序开发过程中,很重要的部分就是对程序异常的监控和处理,包括如何触发异常,何时进行处理,如何进行处理,是否将程式中的所有异常集中在一起,通过公共异常处理的procedure或function,如果没有完善的程式处理机制,很难说该程式是一只健壮的程式,当程式遇到很多类型或者量很多资料时,系统若没有异常处理必然会导致程式的出错 当预判到了某些异常,需要对预判到的异常进行合适相应的处理,是否抛出异常还是忽略还是其他 当然程式没有预判到或