专栏首页dotnet & javaWCF 入门(20)

WCF 入门(20)

前言

Happy weekend.

第20集 通过实现IErrorHandler接口来统一处理WCF里的异常 Centralized exception handling in WCF by implementing IErrorHandler interface

今天第20集了。这个视频系列里面有6集和异常相关,这集是最后一集。前面几集讲了服务端遇到普通的 .net exception时候,要转换城Soap Fault,用fault Exception 或 FaultException<T>来处理。 上一集的例子中用了在主方法体上加大的try catch块来捕获异常,然后throw成FaultExcepiton,这个有个坏处,我们不可能在所有的方法上都加上这么一段,因为不仅代码上显得臃肿,而且加起来麻烦,到处的try-catch。这集就通过IErrorHandler接口来提供一种相对优雅很多的方法。

在ASP.net 的web程序中,我们可以用Global.asax中的Application_Error()事件来记录异常日志,然后处理掉比如redirect到其他自定义错误页什么的。

WCF中,我们可以用IErrorHandler 接口来实现类似的功能。

总共有3步:

1. 创建一个实现了IErrorHandler 接口的类。

    public class GlobalErrorHandler : IErrorHandler
    {
        public bool HandleError(Exception error)
        {
            //can do something like log or what.
            //true means this error was handled.
            return true;
        }

        public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
        {
            if(error is FaultException) return;

            var faultException = new FaultException("A general service error occured!");
            var msgFault = faultException.CreateMessageFault();
            fault = Message.CreateMessage(version, msgFault, null);
        }
    }

定义一个类,GlobalErrorHandler,实现IErrorHandler接口。 这个接口里面有两个方法,分个介绍:

HandleError: 这个返回一个true or false,表示这个Exception是否已经被处理。通常,我们也可以在里面做些日志什么的。

ProvideFault: 这个用来构造一个我们需要的FaultException,来避免channel的失效。大致意思是如果error已经是FaultException了,就直接return。 否则,通过传进来的参数,构造一个新的Message,然后赋值给这个ref修饰fault,回传回去。

当Exception 发生时,先进入ProvideFault方法,然后直接return 出这个FaultException给客户端,避免客户端的等待。同时,异步调用HandleError方法。这就是为什么log 要写在HandleError里面而不是写在ProvideFault里面的原因。

2. 创建一个ServiceBehaviour 特性类来告诉WCF服务端当发生异常时,我们要使用上一步创建的GlobalErrorHandler 类。

代码如下:

    public class GlobalErrorHandlerBehaviourAttribute : Attribute, IServiceBehavior
    {
        private readonly Type errorHandlerType;
        public GlobalErrorHandlerBehaviourAttribute(Type errorHandlerType)
        {
            this.errorHandlerType = errorHandlerType;
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            IErrorHandler handler = (IErrorHandler)Activator.CreateInstance(this.errorHandlerType);
            foreach(ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers) {
                var channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                if(channelDispatcher != null) channelDispatcher.ErrorHandlers.Add(handler);
            }
        }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
            //throw new NotImplementedException();
        }
        public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
            //throw new NotImplementedException();
        }
    }

定义了一个GlobalErrorHandlerBehaviourAttribute类,这是一个特性类,同时,实现了IServiceBehavior接口。定一这个类的构造函数,传入一个类型,确切的说是实现了第一步的IErrorHandler接口类型的类的类型。(或许可以把这个类定义成一个泛型类)

msdn看了一下IServiceBehavior 接口,

提供一种在整个服务内修改或插入自定义扩展的机制,包括 ServiceHostBase。

这个接口定义了3个方法,这里需要关注的是ApplyDispatchBehavior方法。首先通过Activator构造出一个我们需要的ErrorHandler实例(确切的说就是那个GlobalErrorHandler),然后获取当前ServiceHostBase里面的所有的ChannelDispatcher,然后给每个ChannelDispatcher的ErrorHandler添加我们自定义的GlobalErrorHandler。

关于这个ChannelDispatcher,msdn上是这么说的

接受通道以及将通道与服务相关联的组件。

然后msdn上这个ChannelDispacher.ErrorHandlers的解释:

获取 IErrorHandler 对象的集合,这些对象可用于插入终结点的自定义错误处理功能。

3. 把我们定义的CalculatorService 用这个GlobalErrorHandlerBehavior修饰。

    [GlobalErrorHandlerBehaviour(typeof(GlobalErrorHandler))]
    public class CalculatorService : ICalculatorService
    {
        public double Divide(int numerator, int denominator)
        {
            //try {
            return numerator / denominator;
            //} catch(DivideByZeroException ex) {
            //    throw new FaultException<DivideByZeroFault>(new DivideByZeroFault()
            //    {
            //        Error = ex.Message,
            //        Details = "Denominator can't be zero"
            //    });
            //} catch(Exception ex) {
            //    throw new FaultException(ex.Message, new FaultCode("Unknow Code"));
            //}
        }
    }

同时,我们移除所有这个大的try-catch块,因为他本来就不应该出现这里。如果有需要,可以把这个移到第一步的ProvideFault方法里面,在里面判断出现的Exception是不是DivideByZeroException(common .net Exception,不是FaultException,如果是的话可以抛出一个我们定义的DivideByZeroFault)。

下面来测试一下

host起服务,然后更新一下客户端的服务引用。然后输入除数和被除数:

如图我们得到了A general serice error occurred! 的错误消息,并且,再次输入非0的除数也可以得到正确的结果。

这集就是这样,讲的是IErrorHandler接口的使用。如果是在WCF的实际项目中应该还是比较好用的吧。

Thank you。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • [java 基础]反射入门

    使用java的反射,可以让我们检查(或者修改)类,接口,字段,方法的特性。当你在编译期不知道他们的名字的时候非常有用。

    _淡定_
  • 讲一下Asp.net core MVC2.1 里面的 ApiControllerAttribute

    ASP.NET Core MVC 2.1 特意为构建 HTTP API 提供了一些小特性,今天主角就是 ApiControllerAttribute. (注:文...

    _淡定_
  • 用Portable.BouncyCastle来进行加解密的代码demo

    这里对之前对接的公司中的代码demo做一个总结,原本为清一色的java,哈哈。这里都转成C#。用到的库是Portable.BouncyCastle。官网。之前也...

    _淡定_
  • php设计模式

    什么是设计模式 设计模式,是一种解决问题的思维,而并非某种特定的方法。是前人给我们总结的宝贵经验。学习设计模式是为了编写可复用、可拓展、高性能软件。学习设计模式...

    joshua317
  • EasyExcel快速读写Excel数据

    ps: 其实本人并没有对比过POI,只是网络上资料都有这么一说,再对比了GitHub的star数,最后直接选用了EasyExcel

    十毛
  • 如何定位Obj-C野指针随机Crash(三):如何让Crash自报家门

    本文主要介绍如何利用OC Runtime的特性,让OC野指针对象主动抛出自己的信息,秒杀某些全系统栈Crash。 ? 陈其锋,腾讯SNG即通产品部音视频技术中心...

    腾讯Bugly
  • SpringCloud技术指南系列(十)配置管理之自建配置中心

    详细可以查看《SpringBoot入门建站全系列(二十三)配置文件优先级及常用配置方式》.

    品茗IT
  • JAVA移动支付微信和支付宝后台代码

    前言:之前接APP支付,微信遇到了一点点坑,为了方便以后copy,把之前写的代码粘贴出来。需要的同学可以参考一下,具体参数说明还请详细查看官方文档:

    王念博客
  • 【译】用纯JavaScript写一个简单的MVC App

    我想使用model-view-controller体系结构模式并用纯JavaScript编写一个简单的应用程序。所以我着手做了,下面就是。希望能帮你理解MVC,...

    嘉明
  • phalcon-入门篇4(log日志和session缓存)

    #phalcon-入门篇4(log日志和session缓存)# ? 本教程基于phalcon2.0.9版本 ##前言## 先在这里感谢各位phalcon技术爱好...

    喵了个咪233

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动