原文: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. BCS--设置BDC元数据存储权限--访问被业务数据拒绝

    设置元数据存储权限 http://blog.163.com/liangshan_wei@126/blog/static/8297850320139126930290/

  2. F - 蜘蛛牌(深度搜索)

    Problem Description 蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的牌时,那么 ...

  3. Python每日一练(2):找出html中的所有链接(Xpath、正则两个版本)

    要在hrml文件中找出特定的内容,首先需要观察该内容是什么东西,在什么位置,这样才能找出来. 假设html的文件名称是:"1.html".href属性全都在a标签里. 正则版: # ...

  4. Regex阅读笔记(三)之固化分组

    符号:?> 使用?>的匹配与正常的匹配无区别,但是如果匹配进行到此结构之后,此结构体的所有备用状态都会放弃,也就是括号内的子表达式中未尝试过的备用状态都不复存在了. 例如'(\.\d\d( ...

  5. TextView textSize 文字大小

    TextView,很常见的控件.关于文字大小的方法有: android.widget.TextView#getTextSize  返回值的单位是PX /** * @return the size (i ...

  6. java学习之xml

    xml的处理有两种方式dom和Sax 其中dom有3套api ,分别是dom和jdom和dom4j package com.gh.xml; import java.io.File; import ja ...

  7. stm32之USART通信

    任何USART通信,需要用到2个对外连接的引脚:RxD,TxD: RxD是输入引脚,用于串行数据接收: TxD是输出引脚,用于串行数据发送: SCLK引脚:发生器时钟输出(同步模式下,异步模式下不需要 ...

  8. Android 启动APP时黑屏白屏的三个解决方案(转载)

    你会很奇怪,为什么有些app启动时,会出现一会儿的黑屏或者白屏才进入Activity的界面显示,但是有些app却不会如QQ手机端,的确这里要做处理一下.这里先了解一下为什么会出现这样的现象,其实很简单 ...

  9. 设置outlook自动回复

    当有同事需要出差时,或者不能即时回复邮件时,可用此功能. 下面列出设置步骤: 1.  首先,在桌面新建一个用以保存模板的文件夹,例如:emaii. 2.  新建一封邮件,输入你要自动回复的内容.另存为 ...

  10. 自定义ActionBar背景(分别针对3.0以下和3.0以上的版本)

    官方原文:http://developer.android.com/training/basics/actionbar/styling.html 针对3.0以上的版本: <?xml versio ...