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

[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常。在服务执行过程中,我们手工抛出FaultException异常,WCF服务端框架会对该异常对象进行序列化病最终生成Fault消息。当WCF客户端框架介绍到该Fault消息之后,会做一项相反的操作:对Fault消息中进行解析和反序列化,重新生成并抛出FaultException异常。WCF框架自动为我们作了这么多“幕后”工作,使得开发人员可以完全采用编写一般的.NET应用程序的模式进行异常的处理:在错误的地方抛出相应异常,对于潜在出错的方法调用进行相应的异常捕获和处理。所以,WCF的异常处理框架的核心功能就是实现FaultException异常和Fault消息之间的转换,接下来我们着重来讨论这个话题。

一、FaultException异常和Fault消息之间的纽带:MessageFault

对于WCF的异常处理框架,其本身并不直接进行FaultException异常和Fault消息之间的转换,而是通过另外一个作为中介的对象来完成的,这个对象就是这一小节我们讲述的重点:MessageFault。Message(Fault)、MessageFault和FaultException通过如图1描述的“三角”关系实现了相互之间的转化。

clip_image002

图1 Message(Fault)、Message和FaultException“三角”转换关系

在消息介绍MessageFault之前,我们先来看看MessageFault的定义。MessageFault定义在命名空间System.ServiceModel.Channels下,下面的代码是MessageFault的定义。

   1: public abstract class MessageFault
   2: {
   
   3:    //其他成员
   4:     public static MessageFault CreateFault(Message message, int maxBufferSize);
   5:     public static MessageFault CreateFault(FaultCode code, FaultReason reason);
   6:     public static MessageFault CreateFault(FaultCode code, string reason);
   7:     public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail);
   8:     public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer);
   9:     public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor);
  10:     public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor, string node);
  11:     
  12:     public T GetDetail<T>();
  13:     public T GetDetail<T>(XmlObjectSerializer serializer);
  14:     public XmlDictionaryReader GetReaderAtDetailContents();
  15:  
  16:     public void WriteTo(XmlDictionaryWriter writer, EnvelopeVersion version);
  17:     public void WriteTo(XmlWriter writer, EnvelopeVersion version);
  18:  
  19:     public virtual string Actor { get; }
  20:     public abstract FaultCode Code { get; }
  21:     public abstract bool HasDetail { get; }
  22:     public bool IsMustUnderstandFault { get; }
  23:     public virtual string Node { get; }
  24:     public abstract FaultReason Reason { get; }
  25: }

从上面给出的对MessageFault并不复杂的定义可以看出,它的属性成员和FaultException,以及SOAP Fault的5个子元素是想匹配的:Code、Reason、Node、Actor(对于SOAP 1.2规范中SOAP Fault的Role元素,在SOAP 1.1中的名称为Actor)。而另一个元素Detail则可以通过两个泛型方法GetDetail<T>获得。由于此操作需要对错误明细对象进行反序列化,所以需要指定错误明细类型对应的序列化器,默认情况下采用的是DataContractSerializer。而属性IsMustUnderstandFault表述此错误是否是由于识别 SOAP 标头失败而造成的,实际上,它和FaultCode的IsPredefinedFault向对应,主要具有预定义的Code,IsMustUnderstandFault就返回True。

通过MessageFault众多的CreateFault静态方法,我们可以以不同的组合方式指定构成SOAP Fault的5个元素。如果指定了错误明细对象,需要指定与之匹配的序列化器以实现对其的序列化和反序列化。两个重载的WirteTo方法实行对MessageFault进行序列化,并将序列化后的XML通过XmlDictionaryWriter或者XmlWriter写入掉相应的“流”中。

由于不同的SOAP规范的版本(SOAP 1.1和SOAP 1.2)对Message Fault的结构进行了不同的规定,所有在调用WirteTo的时候需要显式地指定基于那个版本进行写入(SOAP的版本通过EnvelopeVersion表示)。下面的示例代码中,我们创建了一个MessageFault对象,分别针对SOAP 1.1和SOAP 1.2写到两个不同的XML文件中。读者可以仔细辨别最终生成的Message Fault到底有多大的差别。

   1: using System.Collections.Generic;
   2: using System.Diagnostics;
   3: using System.IO;
   4: using System.Runtime.Serialization;
   5: using System.ServiceModel;
   6: using System.ServiceModel.Channels;
   7: using System.Text;
   8: using System.Xml;
   9: namespace MessageFaultDemos
  10: {
   
  11:     class Program
  12:     {
   
  13:         static void Main(string[] args)
  14:         {
   
  15:             FaultCode code = FaultCode.CreateSenderFaultCode(new FaultCode("CalculationError", "http://www.artech.com/"));
  16:             IList<FaultReasonText> reasonTexts = new List<FaultReasonText>();
  17:             reasonTexts.Add(new FaultReasonText("The input parameter is invalid!","en-US"));
  18:             reasonTexts.Add(new FaultReasonText("输入参数不合法!", "zh-CN"));
  19:             FaultReason reason = new FaultReason(reasonTexts);
  20:  
  21:             CalculationError detail = new CalculationError("Divide", "被除数y不能为零!");
  22:             MessageFault fault = MessageFault.CreateFault(code, reason, detail, new DataContractSerializer(typeof(CalculationError)), "http://http://www.artech.com/calculatorservice", "http://http://www.artech.com/calculationcenter");
  23:  
  24:             string fileName1 = @"fault.soap11.xml";
  25:             string fileName2 = <
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值