今天学习WCF分布式开发步步为赢系列的15节:错误契约(FaultContract)与异常处理(ExceptionHandle)。本节内容作为 WCF分布式开发的一个重要知识点,无论在学习还是项目中都应该有所了解。此前也和多位学习爱好者讨论过WCF异常处理的相关知识。这里就系统整理一下, 共大家参考。同时也是对《WCF分布式开发步步为赢》系列文章的完善和补充。
   本节主要涉及的知识点就是:【1】.NET异常处理【2】WCF异常处理【3】错误契约【4】WCF异常处理扩展【5】示例代码分析,最后是【6】总结部分。
   首先我们来回忆一下.NET里一个重要的概念异常处理ExceptionHandle。异常处理在JAVA平台也有自己的机制,这个不是一个WCF特有的概念,同样要了解WCF的异常处理,我们有必要先来了解其前身.NET的异常处理相关的概念。
【1】.NET异常处理:
     .NET Framework 中的托管异常是凭借 Win32 结构化异常处理机制实现的。公共语言运行库提供了一个模型,以统一的方式通知程序发生的错误,这样为设计容错软件提供了极大的帮助。所有 的.NET Framework操作都通过引发异常来指示出现错误。 传统上,语言的错误处理模型依赖于语言检测错误和查找错误处理程序的独特方法,或者依赖于操作系统提供的错误处理机制。运行库实现的异常处理具有下列特 点:     
        处理异常时不用考虑生成异常的语言或处理异常的语言。     
        异常处理时不要求任何特定的语言语法,而是允许每种语言定义自己的语法。     
        允许跨进程甚至跨计算机边界引发异常。     
        与 其他错误通知方法(如返回代码)相比,异常具有若干优点。不再有出现错误而不被人注意的情况。无效值不会继续在系统中传播。不必检查返回代码。可以轻松添 加异常处理代码,以增加程序的可靠性。最后,运行库的异常处理比基于Windows 的C++错误处理更快。   
    由于执行线程例行地遍历托管代码块和非托管代码块,因此运行库可以在托管代码或非托管代码中引发或捕捉异常。非托管代码可以同时包含C++样式的   SEH   异常和基于COM 的HRESULT。 
     异常处理使用 trycatchfinally 关键字尝试某些操作,以处理失败情况,尽管这些操作有可能失败,但如果您确定需要这样做,且希望在事后清理资源,就可以尝试这样做。公共语言运行时 (CLR)、.NET Framework 或任何第三方库或者应用程序代码都可以生成异常。异常是使用 throw 关键字创建的。(MSDN)
【2】WCF异常处理:
   现在我们来了解一下WCF的异常处理机制。 前面我们介绍了.NET的异常处理机制。WCF也是.NET框架的一部分,很多一场处理方式基本相同。但是由于其跨服务平台的目标要求,导致了WCF并不 支持传统的异常处理方式。传统方式上.NET抛出的未经处理的异常会立即终止主进程,而WCF则不会。
【2.1】WCF错误类型:
   在进行WCF分布式应用开发的过程中,我们客户端经常会遇到一下三种常见的错误。
(1):通信错误,可能和网络、通道等相关的异常,客户端表现为Communication Exception;
(2):代理和通道的State,代理已经关闭,或者通道Fault,等问题,这个比较常见。一般通道闲置时间过久,通道会出现这个状态错误的问题。一般我们可以通过代理的State来判断。安全验证失败也会导致这个错误。
(3):服务调用错误,服务调用时抛出的异常,这个服务内部异常会序列化传递给客户端,被客户端捕获。
    第三种是我们本节要详细讲述的类型。
【2.2】FaultException:
   这里最关键的问题就是,WCF服务抛出的异常信息往往是基于.NET的内部异常信息,这些信息不能被序列化,也就不能在客户端和服务端实现共享错误信息。
    因此如果在WCF服务中采用传统的方式处理异常,由于异常消息不能被序列化,因而客户端无法捕获和处理服务抛出的异常。为了解决这个问题,WCF提供了 FaultException。这个是一个基于行业标准的SOAP异常类,WCF会将无法识别的异常处理为统一的FaultException异常对象, 因此,可以把错误信息传递到客户端,客户端可以捕获FaultException或者Exception。你可以通过.NET Reflector查看此类的定义,FaultException的部分代码如下:

[Serializable, KnownType(typeof(FaultReasonData[])), KnownType(typeof(FaultCodeData)), KnownType(typeof(FaultCodeData[])), KnownType(typeof(FaultReasonData))]
public class FaultException : CommunicationException
{
    // Fields
    private string action;
    private FaultCode code;
    private MessageFault fault;
    internal const string Namespace = "http://schemas.xmlsoap.org/Microsoft/WindowsCommunicationFoundation/2005/08/Faults/";
    private FaultReason reason;

// Methods
    public FaultException();
}

我们可以注意到FaultException具有序列化的标记。声明了Serializable.
FaultException的另外一个重要的泛型定义就是FaultException<T>,这里我们可以使用任何系统类型或者自定义类型来传递错误信息,T代表要传递的错误细节。此类也可以使用反射器查看代码:


[Serializable]
public class FaultException<TDetail> : FaultException
{
    // Fields
    private TDetail detail;

// Methods
    public FaultException(TDetail detail);
    public FaultException(TDetail detail, FaultReason reason);
    public FaultException(TDetail detail, string reason);
    protected FaultException(SerializationInfo info, StreamingContext context);
    public FaultException(TDetail detail, FaultReason reason, FaultCode code);
    public FaultException(TDetail detail, string reason, FaultCode code);
    public FaultException(TDetail detail, FaultReason reason, FaultCode code, string action);
    public FaultException(TDetail detail, string reason, FaultCode code, string action);
    public override MessageFault CreateMessageFault();
    [SecurityCritical, SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter=true)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context);
    public override string ToString();
    // Properties
    public TDetail Detail { get; }
}

【3】错误契约FaultContract:
    说道WCF的异常处理,这里就必须介绍一下一个重要的概念:错误契约(FaultContract)。
   前面已经介绍了WCF的FaultException,那为什么我们还要FaultContract。
   默认情况,WCF会把服务抛出的异常以FaultException类型传递到客户端。
  但是WCF规定,任何客户端和服务共享的异常,必须属于服务行为的特性。也就是我们必须给特性的服务操作定义了服务契约才能够提供错误类型。WCF为异常 处理专门提供了FaultContract特性,它可以被应用到服务操作上,指明操作可能会抛出的异常类型。代码如下:

[AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=false)]
public sealed class FaultContractAttribute : Attribute
{
    // Fields
    private string action;
    private bool hasProtectionLevel;
    private string name;
.
}

错误契约FaultContract其实是WCF4大契约中的一种(Service Contract,DataContract,MessageContract,FaultContract ).声明一个服务操作契约方法为FaultContract并指定了响应的类型参数,这个参数要和FaultException<T>的类型 一致。
【4】WCF异常处理扩展:
WCF允许开发者定制异常报告和异常传递的过程。但是要实现System.ServiceModel.Dispatcher.IErrorHandler接口:
IErrorHandler的定义如下:

public interface IErrorHandler
{
    // Methods
    bool HandleError(Exception error);
    void ProvideFault(Exception error, MessageVersion version, ref Message fault);
}

服务端抛出的异常会再调用ProvideFault()方法后再返回给客户端。ProvideFault不考虑异常的类型。
    另外ProvideFault一个重要的作用,异常提升(Exception Promotion)。它可以将非FaultContract异常提升为FaultContract<T>异常,例如将 OverflowException异常提升为FaultExceptino< OverflowException>异常。而HandleError()方法,我们可以重新实现代码,如log日志。
  此外,如果要安装自己定制的IErrorHandle,需要将它添加到对应的分发器里即可。
   我们在服务类实现System.ServiceModel.Description.IServiceBehavior接口 ApplyDispatchBehavior()方法中来实现安装错误处理扩展。IServiceBehavior的主要方法 ApplyDispatchBehavior:

public interface IServiceBehavior
{
    // Methods
    void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters);
    void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
    void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
}

【5】示例代码分析:
    下面我们给出本节文章的简单示例代码。
【5.1】服务端:
    这里我们定义了两个服务操作方法,主要是为了测试WCF的异常处理,SayHello和SayGoodBye。参数是个string 类型的name,长度大于10的时候就会抛出一个OverflowException内存溢出的异常,然后我们通过FaultException来包装, 传递到客户端。这里我们也对操作契约应用了FaultContract特性。定义代码如下:

//1.服务契约
    [ServiceContract(Namespace = "http://www.cnblogs.com/frank_xl/")]
    public interface IWCFService
    {
        //操作契约
        [OperationContract]
        [FaultContract(typeof(OverflowException))]//标注以后,WCF客户端才能区分错误契约,否则会作为通信异常抛出
        string SayHello(string name);
        //操作契约
        [OperationContract]
        [FaultContract(typeof(OverflowException))]
        string SayGoodBye(string name);
    }
    //2.服务类,继承接口。实现服务契约定义的操作
    public class WCFService : IWCFService
    {
        //实现接口定义的方法
        public string SayHello(string name)
        {
            if (name.Length < 10)
            {
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("Hello! {0},", name);
                return "Hello! " + name;
            }
            else
            {//泛型类FaultException,可以包含不同的异常,可以序列化。基于SOAP的错误消息
                OverflowException oe = new OverflowException();
                throw new FaultException<OverflowException>(oe, "name Length is more than 10");
            }
        }
        //实现接口定义的方法
        public string SayGoodBye(string name)
        {
            if (name.Length < 10)
            {
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("GoodBye! {0},", name);
                return "Hello! " + name;
            }
            else
            {
                OverflowException oe = new OverflowException();
                throw new FaultException<OverflowException>(oe,"name Length is more than 10");
            }
        }
    }

【5.2】宿主:
    这里宿主依然是Console 程序,自托管WCF服务。会打印每次的调用信息。比较简单,代码如下:

static void Main(string[] args)
        {
            //反射方式创建服务实例,
            //Using方式生命实例,可以在对象生命周期结束时候,释放非托管资源
            using (ServiceHost host = new ServiceHost(typeof(WCFService.WCFService)))
            {
                ////判断是否以及打开连接,如果尚未打开,就打开侦听端口
                if (host.State != CommunicationState.Opening)
                    host.Open();
                //显示运行状态
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Host is runing! and state is {0}", host.State);
                Console.ForegroundColor = ConsoleColor.Red;
                //print endpoint information
                foreach (ServiceEndpoint se in host.Description.Endpoints)
                {
                    Console.WriteLine("Host is listening at {0}", se.Address.Uri.ToString());

}
                //等待输入即停止服务
                Console.Read();
            }
        }

【5.3】客户端:
   客户端分别传递两次参数,第一次正常调用,第二次参数长度大于10,引起服务端异常,然后跑出到客户端,客户端根据定义的FaultException<T>来处理异常。代码如下:

//WSHttpBinding_IWCFService
            Test.WCFServiceClient wcfServiceProxyHttp = new Test.WCFServiceClient("WSHttpBinding_IWCFService");
            //通过代理调用SayHello服务
            try
            {
                Console.WriteLine(wcfServiceProxyHttp.SayHello("Frank Xu"));
                Console.WriteLine(wcfServiceProxyHttp.SayHello("Frank Xu Lei WSHttpBinding"));
            }
            catch (FaultException<OverflowException> oe)
            {
                Console.WriteLine(oe.Message);
            }
            catch (CommunicationException ce)
            {
                Console.WriteLine(ce.Message);
            }
            finally 
            { 
                wcfServiceProxyHttp.Close(); 
            }
           
            //For Debug
            Console.WriteLine("Press any key to exit");
            Console.Read();

这里运行程序,测试结果如下:

【6】总结:
    以上就是WCF异常处理的全部内容。一下几点我们要注意:
    (1)  即使我们不加FaultContract特性,或者抛出非FaultException异常,客户端也可以获得异常消息,该异常会导致通道出现错误。
    (2)此外我们也可以通过添加ServiceBehavior特性,将服务的IncludeExceptionDetailInFaults设置为 true(默认为 false),客户端也可以捕获抛出的非FaultException异常信息,但该异常仍然会导致通道出现错误。
    (3)异常对WCF服务实例的影响和服务的激活类型有关。通常单调PerCall和会话模式,WCF服务异常会导致服务释放服务实例,而Single单例模式不会,WCF服务会继续运行。
    (4)在发布服务与部署服务时,我们应避免将服务的IncludeExceptionDetailInFaults设置为true。这回带来性能问题。

http://blog.csdn.net/book_frank_xl/article/details/4735865

错误契约(FaultContract)与异常处理(转)相关推荐

  1. WCF分布式开发步步为赢(15):错误契约(FaultContract)与异常处理(ExceptionHandle)

    今天学习WCF分布式开发步步为赢系列的15节:错误契约(FaultContract)与异常处理(ExceptionHandle).本节内容作为WCF分布式开发的一个重要知识点,无论在学习还是项目中都应 ...

  2. SpootBoot错误和异常处理机制及错误页指派+全局异常处理

    一:错误和异常 Error经常式程序无法处理的(用户引入的错误 和系统错误) Exception程序员引发的,能够在程序中处理(try catch / throws) 二:处理异常的方式 提供三种处理 ...

  3. python3语法错误-【Python3之异常处理】

    一.错误和异常 1.错误 代码运行前的语法或者逻辑错误 语法错误(这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正) def test: ^ SyntaxError: inva ...

  4. python中if错误-python中的异常处理

    异常 异常就是程序运行时发生错误的信号,在python中,错误触发的异常如下 异常种类 在python中不同的异常可以用不同的类型(python中统一了类与类型,类型即类)去标识,不同的类对象标识不同 ...

  5. python语法错误修改_python基础之错误、调试(异常处理)

    在程序运行过程中,总会遇到各种各样的错误. 有的错误是程序编写有问题造成的,比如本来应该输出整数结果输出了字符串,这种错误我们通常称之为bug,bug是必须修复的. 有的错误是用户输入造成的,比如让用 ...

  6. jsp错误处理页面_JSP异常处理– JSP错误页面

    jsp错误处理页面 Exception handling in JSP is done by JSP exception pages. JSP异常页面中完成了JSP中的异常处理. JSP中的异常处理 ...

  7. python中语法错误-Python语法错误与异常及异常处理方法

    回顾 在Python进阶记录之基础篇(二十)中,我们介绍了Python面向对象中的类方法和静态方法,以及类中拥有特殊功能的魔法函数.需要重点掌握类方法和静态方法的概念和基本用法,理解魔法函数的作用冰女 ...

  8. golang错误处理机制(异常处理)

    看一段代码,引入错误处理 对上面代码的总结: 在默认情况下,当发生错误后(panic) ,程序就会退出(崩溃.) 如果我们希望:当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行.还可 以 ...

  9. matlab错误使用assert,关于异常处理:何时在Matlab中使用assert()?

    由于Matlab被解释,通常会在执行函数签名的函数开头花费大量时间. 例如 if nargin ~= 2; error('must provide two input args a and b'); ...

最新文章

  1. 素数环(dfsamp;amp;STL做法)HDU - 1016
  2. Python面试题-交换两个数字的三种方法
  3. 微信小程序 自定义顶部状态栏
  4. 怎么查看页面跳转过程_fastcapture注册码怎么获取?FastStone注册码分享
  5. JNI编程中数据类型转换的方法
  6. bzoj2208: [Jsoi2010]连通数
  7. 初中女生数学不好能学计算机,初中数学成绩好的女生,多半有这3个“小特征”,学渣可装不来!...
  8. 有什么软件可以把视频上下拼接在一起?
  9. java 邮件接收端程序_java邮件收发功能实现代码
  10. 字母冒泡法排序c语言,C语言冒泡排序法的实现(升序排序法)
  11. localhost 127.0.0.1 本机IP
  12. ABB机器人伺服电机维修故障
  13. python不是5的倍数_查找所有低于1000的数字之和,这是Python中3或5的倍数
  14. 为什么保险公司一直不停地招人?
  15. 微信分享、极简微信分享、thinkphp微信分享、laravel微信分享、3分钟完成微信分享
  16. 【烤机向】美国大选有没有打成269票平局的情况?
  17. 前端开发练习——包含了计时功能的动画时钟
  18. PMP思维导图—项目经理的角色
  19. 数据结构实验——哈夫曼编码
  20. 2018计算机网络MOOC第一章作业1

热门文章

  1. 12.15 Ext JS 选人组件
  2. Spring Boot与JWT整合实现前后端分离的用户认证
  3. 在Tomcat 与weblogic 中的 日志(log4j) 配置系列一
  4. JSP自定义标签入门实例
  5. php 获取所有子目录名,php读取目录及子目录下所有文件名的方法,_PHP教程
  6. python作品代码_学习python的一些心得体会
  7. String类的equals()与==的区别
  8. springboot数据源oracle,springboot+mybatis中使用多数据源oracle数据库
  9. python中if语句求最大值_Python中用max()方法求最大值的介绍
  10. map转成url拼接请求参数