从前文中我们了解到,对于服务器端抛出的异常,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文章——IErrorHandler接口,我这里就不多介绍了,一个基本的方式如下:

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);
            }
        }
    }

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

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

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

<extensions>
        <behaviorExtensions>
            <addname="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>子类。

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

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

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

WCF入门(八)——异常处理2相关推荐

  1. [WCF编程]1.WCF入门示例

    一.WCF是什么? Windows Communication Foundation(WCF)是由微软开发的一系列支持数据通信的应用程序框架,整合了原有的windows通讯的 .net Remotin ...

  2. WCF 入门之旅(4): 怎样用客户端调用WCF服务

    WCF 入门之旅(4): 怎样用客户端调用WCF服务 前面的demo已经能够跑起来,现在开始考虑用客户端来测试所写的应用了,首先用个普通的应用程序来调用所写的wcf服务吧.其实最后运行wcf服务后的页 ...

  3. Spring Boot入门——全局异常处理

    Spring Boot入门--全局异常处理 参考文章: (1)Spring Boot入门--全局异常处理 (2)https://www.cnblogs.com/studyDetail/p/702758 ...

  4. [老老实实学WCF] 第八篇 实例化

    老老实实学WCF 第八篇 实例化 通过上一篇的学习,我们简单地了解了会话,我们知道服务端和客户端之间可以建立会话连接,也可以建立非会话连接,通信的绑定和服务协定的 ServiceContract 的S ...

  5. WCF入门的了解准备工作

    了解WCF, 及WCF入门需要掌握哪里基本概念? 1.准备工作 >1.1 . XML >1.2 . Web Service >1.3 . 远程处理 (RPC) >1.4.  消 ...

  6. python文件路径过滤器_SUMO入门(八) - 从Python引入TraCI接口

    SUMO入门(八) - 从Python引入TraCI接口 TraCI TraCI/Interfacing TraCI from Python TraCI命令分为13个部分,它们对应于各个模块: gui ...

  7. 服务端增加WCF服务全局异常处理机制

    转自:http://www.csframework.com/archive/1/arc-1-20150109-2193.htm 服务端增加WCF服务全局异常处理机制,任一WCF服务或接口方式出现异常, ...

  8. node.js入门 - 7.异常处理与多进程开发

    node.js入门 - 7.异常处理与多进程开发 参考文章: (1)node.js入门 - 7.异常处理与多进程开发 (2)https://www.cnblogs.com/softlover/arch ...

  9. 脑残式网络编程入门(八):你真的了解127.0.0.1和0.0.0.0的区别?

    本文由"小姐姐养的狗"原创发布于"小姐姐味道"公众号,原题<127.0.0.1和0.0.0.0地址的区别>,收录时有优化和改动.感谢原作者的分享. ...

  10. SUMO入门(八) - TraCI

    SUMO入门(八) - TraCI TraCI TraCI/Interfacing TraCI from Python TraCI 是 "Traffic Control Interface& ...

最新文章

  1. 【radar】毫米波雷达相关开源项目代码汇总(工具箱、仿真、2D毫米波检测、融合、4D毫米波检测、分割、SLAM、跟踪)(6)
  2. LeetCode - Valid Sudoku
  3. VC++ .Net 实例学习
  4. No module named import_export.admin
  5. GDCM:ReadSelectedTags的测试程序
  6. android webview webp,iOS WebView中使用webp格式图片的方法
  7. 给正在备战秋招的同学一些小建议
  8. 让你人见人爱的27个原则
  9. db4o_8.0对象数据库官方文档翻译_学习笔记一
  10. 2017年节假日放假安排来了!
  11. 杭州好玩景点攻略200
  12. 基于Html+Css+javascript的网页制作——音乐舞蹈学校(5页面) 计算机毕设网页设计源码
  13. 服务器ping不通网址!
  14. 零基础学python难吗?python自学难吗?
  15. PS|001制作1寸照片
  16. 含文档+PPT+源码等]精品基于ssm的足球联赛管理系统的设计与实现vue[包运行成功]计算机Java毕业设计SSM项目源码
  17. vue 打造携程网平台界面实战
  18. java获取GET和POST请求参数
  19. 拆掉思维里的墙-阅读记录
  20. 使用python 520表白吧

热门文章

  1. Queue接口及是实现类PriorityQueue介绍
  2. 008_Gson从序列化中排除字段
  3. 025_Jedis集群
  4. java 小坑_关于Java子父类关系的小坑
  5. 检测移动端内存敏感数据方法(安卓)
  6. hive读取hdfs存放文件_Hive基本概念
  7. linux emule 编译 wx-config --libs,linux下编译wxwidgets所写程序所遇到的问题
  8. 依赖注入Dagger2详解
  9. stackoverflow上Java相关回答整理翻译
  10. Android序列化与反序列化