原文:WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的?

服务端只有抛出FaultException异常才能被正常地序列化成Fault消息,并实现向客户端传播。对于一般的异常(比如执行Divide操作抛出的DivideByZeroException),在默认的情况下,异常信息无法实现向客户端传递。但是,倘若为某个服务应用了ServiceDebugBehavior这么一个服务行为,并开启了IncludeExceptionDetailInFaults开关,异常信息将会原封不动地传播到客户端。WCF内部是如何处理抛出的非FaultException异常的呢?

实际上,WCF对非FaultException异常的处理并不复杂,我们现在就来简单介绍一下相关的流程:在执行服务操作过程中,如果抛出一个非FaultException异常,WCF会先判断IncludeExceptionDetailInFaults开发是否开启,如果没有,WCF会手工创建一个MessageFault对象,并根据当前线程的语言文化从资源文件中获取一段固定的文本作为MessageFaultFaultReason(就是我们在《WCF基本的异常处理模式[上篇]》的例子中看到的那段文字)。此外,固定的FaultCode被创建出来作为该MessageFault的Code。最终,WCF将该MessageFault转换成一个Fault消息,并采用固定的Action作为该消息的Action报头。所以,无论服务端抛出怎样的异常,客户端捕获的总是具有相同信息的FaultException异常。

注:客户端的错误信息总是这么一段文字:“由于内部错误,服务器无法处理该请求。有关该错误的详细信息,请打开服务器上的 IncludeExceptionDetailInFaults (从 ServiceBehaviorAttribute 或从 <serviceDebug> 配置行为)以便将异常信息发送回客户端,或在打开每个 Microsoft .NET Framework 3.0 SDK 文档的跟踪的同时检查服务器跟踪日志。”

上面说的是IncludeExceptionDetailInFaults开关关闭的情况。如果IncludeExceptionDetailInFaults开启,WCF则会基于该异常对象创建ExceptionDetail对象,并将该对象作为明细对象创建MessageFault(采用固定FaultCode)。最终,将此MessageFault转换生成Fault消息,当然Action也是采用固定的预定值。因此,在这种情况下,服务端抛出的信息总是能够原封不动地传递到客户端。而客户端捕获的总是一个泛型的FaultException<ExceptionDetail>异常。

一、ExceptionDetail对象为何能被反序列化?

对于异常对象的序列化和反序列化工作,最终都回落在FaultFormatter这么一个对象上(具体原理,可以参考《深入剖析WCF底层异常处理框架实现原理[中篇]》)。无论是虚列化还是反序列化,都具有一个根本的前提:确定对象的类型。FaultFormatter依赖创建时指定的一个FaultContractInfo列表来获知具体的类型,而该列表最初来源于应用在操作方法上FaultContractAttribute特性的定义。

那么,对于应用了ServiceDebugBehavior服务行为,并开启了IncludeExceptionDetailInFaults的场景,客户端如何能够把承载与Fault消息中的表示错误明细的XML片段通过反序列化生成ExceptionDetail对象的呢?由于我们不曾通过FaultContractAttribute特性将ExceptionDetail类型应用在相应的操作方法上面,FaultFormatter无法确定反序列化对象的类型,照理说反序列化是无法成功的,这是为何呢?

其实原因很简单,WCF在初始化FaultFormatter的时候会基于ExceptionDetail类型创建FaultContractInfo对象,并将其添加到属于自己的FaultContractInfo列表中。相应的实现基本上可以通过下面的伪代码体现:

   1: internal class FaultFormatter : IClientFaultFormatter, IDispatchFaultFormatter

   2: {

   3:     //其他成员

   4:     private FaultContractInfo[] faultContractInfos;

   5:     internal FaultFormatter(SynchronizedCollection<FaultContractInfo> faultContractInfoCollection)

   6:     {

   7:         List<FaultContractInfo> list= new List<FaultContractInfo>(faultContractInfoCollection);

   8:         faultContractInfoCollection.Add(new FaultContractInfo("http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher/fault", typeof(ExceptionDetail)));

   9:         this.faultContractInfos = list.ToArray();

  10:     }    

  11: }

二、如果直接抛出FaultException<ExceptionDetail>异常呢?

既然FaultFormatter能够自动实现基于ExceptionDetail对象的序列化和反序列化,那么就意味着我们可以在具体的服务操作中直接抛出FaultException<ExceptionDetail>异常,而无须再将ExceptionDetail作为错误契约类型通过FaultContractAttribute特性应用到相应的服务操作上面了。同样以我们的计算服务为例,在Divide方法中我们直接用ExceptionDetail封装在运算过程中抛出的异常,最终抛出FaultException<ExceptionDetail>异常。

   1: using System.ServiceModel;

   2: using Artech.WcfServices.Contracts;

   3: using System;

   4: namespace Artech.WcfServices.Services

   5: {

   6:     [ServiceBehavior(Namespace="http://www.artech.com/")]

   7:     public class CalculatorService : ICalculator

   8:     {

   9:         public int Divide(int x, int y)

  10:         {            

  11:             try

  12:             {

  13:                 return x / y;

  14:             }

  15:             catch (Exception ex)

  16:             {

  17:                 throw new FaultException<ExceptionDetail>(new ExceptionDetail(ex), ex.Message);

  18:             }

  19:         }

  20:     }

  21: }

在客户端,我们就可以直接捕获FaultException<ExceptionDetail>异常了。下面的代码中,我们将捕获的FaultException<ExceptionDetail>异常相关的信息打印出来:

   1: using System;

   2: using System.ServiceModel;

   3: using Artech.WcfServices.Contracts;

   4: namespace Artech.WcfServices.Clients

   5: {

   6:     class Program

   7:     {

   8:         static void Main(string[] args)

   9:         {

  10:             using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(

  11:                "calculatorservice"))

  12:             {

  13:                 ICalculator calculator = channelFactory.CreateChannel();

  14:                 using (calculator as IDisposable)

  15:                 {

  16:                     try

  17:                     {

  18:                         int result = calculator.Divide(1, 0);

  19:                     }

  20:                     catch (FaultException<ExceptionDetail> ex)

  21:                     {

  22:                         Console.WriteLine("Action: {0}", ex.Action);

  23:                         Console.WriteLine("Code: {0}:{1}", ex.Code.Namespace,ex.Code.Name);

  24:                         Console.WriteLine("Detail");

  25:                         Console.WriteLine("\tMessage: {0}", ex.Detail.Message);

  26:                         Console.WriteLine("\tType: {0}", ex.Detail.Type);

  27:                     }

  28:                 }

  29:             }

  30:             Console.Read();

  31:         }

  32: }

  33: }

输出结果(注意Action正好和在FaultFormatter中指定的值是一致的):

   1: Action: http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher/fault

   2: Code:http://www.w3.org/2003/05/soap-envelope:Sender

   3: Detail:

   4:     Message:试图除以零。

   5:     Type:System.DivideByZeroException

作者:Artech
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的?的更多相关文章

  1. WCF技术剖析之二十五: 元数据(Metadata)架构体系全景展现[元数据描述篇]

    原文:WCF技术剖析之二十五: 元数据(Metadata)架构体系全景展现[元数据描述篇] 在[WS标准篇]中我花了很大的篇幅介绍了WS-MEX以及与它相关的WS规范:WS-Policy.WS-Tra ...

  2. WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]

    原文:WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇] 在[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常.在服务执行过 ...

  3. WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?

    原文:WCF技术剖析之二十: 服务在WCF体系中是如何被描述的? 任何一个程序都需要运行于一个确定的进程中,进程是一个容器,其中包含程序实例运行所需的资源.同理,一个WCF服务的监听与执行同样需要通过 ...

  4. WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]

    原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...

  5. WCF技术剖析之二十八:自己动手获取元数据[附源代码下载]

    原文:WCF技术剖析之二十八:自己动手获取元数据[附源代码下载] 元数据的发布方式决定了元数据的获取行为,WCF服务元数据架构体系通过ServiceMetadataBehavior实现了基于WS-ME ...

  6. WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[扩展篇]

    原文:WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[扩展篇] 通过<实现篇>对WSDL元素和终结点三要素的之间的匹配关系的介绍,我们知道了WSDL的Binding ...

  7. WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇]

    原文:WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇] 元数据的导出就是实现从ServiceEndpoint对象向MetadataSet对象转换的过程,在WCF元数据框 ...

  8. WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序)

    原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序) 基于HTTP-GET的元数据发布方式与基于WS-MEX原理类似,但是ServiceMetad ...

  9. WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序)

    原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序) 通过<如何将一个服务发布成WSDL[编程篇]>的介绍我们知道了如何可以通过编程或者配 ...

随机推荐

  1. WRTnode 的 HTTP Web 开关实验(2016-05-16)

    前言 这里是节取自 物联网的任意门——WRTnode2R 评测 中的 http web 开关灯实验,所以有一些前置设置如果没有描述清楚可参考该处. 正文 步骤一:编辑一个 html 文件,放在 /ww ...

  2. BZOJ 1059: [ZJOI2007]矩阵游戏( 匈牙利 )

    只要存在N个x, y坐标均不相同的黑格, 那么就一定有解. 二分图匹配, 假如最大匹配=N就是有解的, 否则无解 ------------------------------------------- ...

  3. 【c语言】求最大值

    一.我个人觉得求最大值比较简单的一种方法(当然同时求最大值和最小值时稍微改改也能行) #include <stdio.h> int main(void) { int f, i, max; ...

  4. UnixShell编程(第三版)

    这本书相当老了,04年的,现在  在linux上做实验. 1,date 显示日期. 2,who  显示用户,who am i 3,echo 后面字符串会全部输出,,会过滤掉多余空格,单双引号,分号等. ...

  5. VC程序快速删除自己(可能做升级程序的时候有用)

    项目一般都会带有卸载程序,如果这个程序是自己来做的话,在调用完卸载程序后需要删除自己的所有文件,在Google了好久终于找到一些相关信息,一般只能删除一个文件,经过自己的处理,可以删除文件夹下面所有内 ...

  6. Qt下如何修改文件的时间(全平台修改)

    提供一个全平台修改文件的时间的方法,希望大家喜欢 /* UTIME.C: This program uses _utime to set the * file-modification time to ...

  7. Android Camera调用过程分析

    源代码版本:allwinner 4.0.4 frameworks代码: frameworks/base/core/java/android/hardware/Camera.java JNI层代码: f ...

  8. 谷歌三大核心技术(三)Google BigTable中文版

    谷歌三大核心技术(三)Google BigTable中文版 Bigtable:一个分布式的结构化数据存储系统 译者:alex 摘要 Bigtable是一个分布式的结构化数据存储系统,它被设计用来处理海 ...

  9. StringIO模块字符串的缓存

    StringIO经常被用来作为字符串的缓存,应为StringIO有个好处,他的有些接口和文件操作是一致的,也就是说用同样的代码,可以同时当成文件操作或者StringIO操作.比如: import st ...

  10. 解题报告 HDU1176 免费馅饼

    免费馅饼 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...