博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WCF入门(八)——异常处理2
阅读量:5373 次
发布时间:2019-06-15

本文共 4164 字,大约阅读时间需要 13 分钟。

从前文中我们了解到,对于服务器端抛出的异常,WCF框架会把它封装成FaultException返回给客户端。系统自己封装的FaultException携带的信息往往不够,为了通知客户端更多异常信息,需要我们在代码中主动抛出FaultException。

    public class Service1 : IService1

    {
        public void ErrorTest()
        {
            try
            {
                throw new InvalidOperationException("
异常测试");
            }
            catch (Exception e)
            {
                throw new FaultException(e.Message);
            }
        }
    }

这种方式需要在所有的接口都实现,一来麻烦,而来也增加了代码的复杂度。实际上,既然WCF框架本身就提供了封装异常的功能,我们如果接管这个异常封装的过程,那么就可以以一种统一的方式封装FaultException,即能重新封装FaultException提供详细的错误信息,又不用修改服务代码

实际上,WCF本身就提供了让我们自己接管异常处理的接口IErrorHandler,关于的IErrorHandler处理方式和实现说明,请参看MSDN文章——,我这里就不多介绍了,一个基本的方式如下:

    public class ErrorHandler : IErrorHandler

    {
        public bool HandleError(Exception error)
        {
            return false;
        }
        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            var ex = new FaultException(error.Message);
            var mf = ex.CreateMessageFault();
            fault = Message.CreateMessage(version, mf, ex.Action);
        }
    }

ProvideFault接口里面可以实现把代码中的异常封装成FaultException。

在这里只是实现了这个接口,还没有地方调用,无法生效。要使用这个接口,还需要把它和ServiceBehavior关联起来,在IServiceBehavior对象的ApplyDispatchBehavior中关联ErrorHanlder,一般有两种方式:

  1. Service实现
    IServiceBehavior
    接口,在代码中显示关联,创建Service时则自动关联ErrorHanlder
  2. 新建对象实现
    IServiceBehavior
    接口,在配置文件中将其和Service关联起来

下面就分别介绍下这两中实现方式:

代码中显示关联

代码中显示关联的方式比较简单,直接在Service中实现IServiceBehavior接口即可。

    public class Service1 : IService1 ,IServiceBehavior

    {
        public void ErrorTest()
        {
            throw new InvalidOperationException("
异常测试");
        }
        #region IServiceBehavior
成员
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            var errorHanlder = new ErrorHandler();
            foreach (ChannelDispatcher chanDisp in serviceHostBase.ChannelDispatchers)
            {
                chanDisp.ErrorHandlers.Add(errorHanlder);
            }
        }
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }
        #endregion
    }

这种方式比较简单,但得Service的逻辑变得复杂化,另外也不够灵活,我们通常更多的时候使用配置文件中关联的形式。

配置文件中关联

配置文件并不能直接处理IserviceBehavior对象,它只能处理BehaviorExtensionElement对象。因此这种方式下,需要实现三个接口。

  1. IErrorHandler
    对象中
    ProvideFault
    的函数中实现异常封装
  2. IServiceBehavior
    对象中实现对
    IErrorHandler
    的绑定
  3. BehaviorExtensionElement
    对象中返回
    IServiceBehavior
    对象

这里之所以分为三个接口/类,主要是为了更加灵活,像前面IErrorHandler的和IServiceBehavior就可以在直接代码中显示关联的方式中使用。

    class ServiceBehavior : IServiceBehavior

    {
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            var errorHanlder = new ErrorHandler();
            foreach (ChannelDispatcher chanDisp in serviceHostBase.ChannelDispatchers)
            {
                chanDisp.ErrorHandlers.Add(errorHanlder);
            }
        }
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }
    }
    public class ErrorHandlerElement : System.ServiceModel.Configuration.BehaviorExtensionElement
    {
        protected override object CreateBehavior()
        {
            return new ServiceBehavior();
        }
        public override Type BehaviorType
        {
            get
            {
                return typeof(ServiceBehavior);
            }
        }
    }

在实现时,也按照习惯自由实现几个接口,并非一定要实现三个对象,我通常就通常将IServiceBehaviorIErrorHandler合并到一个对象中实现。

代码实现后,然后就需要在config文件中使用ErrorHandlerElement了。主要增加如下两个地方的配置:

    1. 在behaviorExtensions中增加刚才写的ErrorHandlerElement

    <extensions>

        <behaviorExtensions>
            <add
name="errorHanlder" type="WcfService.ErrorHandlerElement, WcfService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        </behaviorExtensions>
    </extensions>

    2. 在serviceBehaviors中增加behavior(注意:这里的behavior需要是当前生效的behavior,如果不是则还需要关联behavior)。

    <serviceBehaviors>

        <behavior name="">
            
<errorHanlder />
        </behavior>
    </serviceBehaviors>

配置好后,重新编译运行,则就可以看到ErrorHanlder的效果了。

增强异常信息

通过上面的方法处理后,我们就可以一种通用的方式来封装FaultException了,但由于这里是通用的方式,也存在通用方式的缺点:不能对具体异常提供细化的信息

这个问题并不容易解决,要提供具体细化的异常信息,最靠谱的方法还是捕获异常,封装成FaultException<T>子类。

不过,在文章中提供了一个通用的的序列化异常的方法,将异常序列化后传递到客户端,客户端就可以直接以像捕获本地异常那样捕获远程异常了,而不用捕获FaultException。

不过,这个方法也有一点问题,那就是要求客户端也是.net语言实现的,否则其它的语言是无法反序列化CLR异常的,失去了语言无关的特性。

转载于:https://www.cnblogs.com/TianFang/archive/2013/01/03/2842744.html

你可能感兴趣的文章
1305: [CQOI2009]dance跳舞 - BZOJ
查看>>
hash储存机制
查看>>
OpenLayers绘制图形
查看>>
洛谷 P1991 无线通讯网
查看>>
数据库第1,2,3范式学习
查看>>
《Linux内核设计与实现》第四章学习笔记
查看>>
Docker 安装MySQL5.7(三)
查看>>
CSS: caption-side 属性
查看>>
windows超过最大连接数解决命令
查看>>
12个大调都是什么
查看>>
angular、jquery、vue 的区别与联系
查看>>
FFmpeg进行视频帧提取&音频重采样-Process.waitFor()引发的阻塞超时
查看>>
最近邻与K近邻算法思想
查看>>
【VS开发】ATL辅助COM组件开发
查看>>
《演说之禅》I &amp; II 读书笔记
查看>>
thinkphp3.2接入支付宝支付接口(PC端)
查看>>
C#中Monitor和Lock以及区别
查看>>
【NOIP2017】奶酪
查看>>
5.6.3.7 localeCompare() 方法
查看>>
Linux下好用的简单实用命令
查看>>