`
varsoft
  • 浏览: 2430468 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

[原创]WCF技术剖析之十七:消息(Message)详解(上篇)

阅读更多

[爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道《天天山海经》为此录制的节目视频(苏州话)]]消息交换是WCF进行通信的唯一手段,通过方法调用(Method Call)形式体现的服务访问需要转化成具体的消息,并通过相应的编码(Encoding)才能通过传输通道发送到服务端;服务操作执行的结果也只能以消息的形式才能被正常地返回到客户端。所以,消息在整个WCF体系结构中处于一个核心的地位,WCF可以看成是一个消息处理的管道。

尽管消息在整个WCF体系中具有如此重要的意义,可是一般的WCF编程人员,却意识不到消息的存在。原因很简单,WCF设计的目标就是实现消息通信的所有细节,为最终的编程人员提供一个完全面向对象的编程模型。所以对于一般的编程人员来说,他们面对的是接口,却不知道服务契约对于服务的描述;面对的是数据类型,却不知道数据契约对序列化的作用;面对的是方法调用和返回值的获取,却不了解底层消息交换的过程。

鼓励大家深入了解WCF关于消息处理的流程具有两个目的:第一,只有在对整个消息处理流程具有清晰认识的基础上才能写出高质量的WCF程序。第二,WCF是一个极具可扩展性的通信框架,可以灵活地创建一些自定义WCF扩展(WCF Extension)以实现你所需要的功能。如同WCF的插件一样,这些自定义的WCF扩展以即插即用的方式参与到WCF整个消息处理流程之中。了解WCF整个消息处理流程是灵活进行WCF扩展的前提。

在WCF中,定义了一个System.ServiceModel.Channels.Message类,用以表示这些具有不同表现形态的消息。在本篇文章中,我们会着重来讨论这个Message类型。首先来介绍消息的版本。

一、消息版本(Message Version)

由于消息基于不同的格式或者结构,不同的格式决定了对消息不同的处理方式,所以对一个消息进行正确处理的前提是确定消息的格式或结构。在WCF中消息的格式与结构由消息的版本决定,在Message中定义了一个类型为MessageVersion的Version属性来表示消息的版本。

   1: public abstract class Message : IDisposable
<!--CRLF-->
   2: {    
<!--CRLF-->
   3:     //其他成员
<!--CRLF-->
   4:     public abstract MessageVersion Version { get; }
<!--CRLF-->
   5: }
<!--CRLF-->

MessageVersion类型定义在System.ServiceModel.Channels命名空间下。由于SOAP规范的版本和WS-Addressing规范的版本是决定消息格式与结构的两个主要因素,所以,MessageVersion由SOAP规范和WS-Addressing规范共同决定。WCF通过System.ServiceModel.EnvelopeVersion和System.ServiceModel.AddressingVersion两个类分别定义SOAP规范的版本和WS-Addressing的版本。

MessageVersion中定义两个静态的方法CreateVersion用以创建相应的MessageVersion对象,两个属性Envelope和Addressing分别表示通过EnvelopeVersion和AddressingVersion体现的SOAP规范版本和WS-Addressing规范版本。

   1: public sealed class MessageVersion
<!--CRLF-->
   2: {    
<!--CRLF-->
   3:     //其他成员
<!--CRLF-->
   4:     public static MessageVersion CreateVersion(EnvelopeVersion envelopeVersion);
<!--CRLF-->
   5:     public static MessageVersion CreateVersion(EnvelopeVersion envelopeVersion, AddressingVersion addressingVersion);    
<!--CRLF-->
   6: 
<!--CRLF-->
   7:     public AddressingVersion Addressing { get; }
<!--CRLF-->
   8:     public EnvelopeVersion Envelope { get; }
<!--CRLF-->
   9: }
<!--CRLF-->

到目前为止SOAP和WS-Addressing各有两个版本:SOAP 1.1 和SOAP1.2, WS-Addressing 2004和WS-Addressing 1.0。它们分别通过定义在EnvelopeVersion和AddressingVersion中相应的静态只读属性表示。Soap11和Soap12代表SOAP 1.1和SOAP1.2,而WSAddressingAugust2004和WSAddressing10则表示WS-Addressing 2004和WS-Addressing 1.0。EnvelopeVersion.None表示消息并非一个SOAP消息,比如非XML结构的消息(比如基于JSON格式)以及POX(Plain Old XML)消息。AddressingVersion.None则表示消息不遵循WS-Addressing规范,比如通过手工方式解决寻址问题。

   1: public sealed class EnvelopeVersion
<!--CRLF-->
   2: {   
<!--CRLF-->
   3:     //其他成员
<!--CRLF-->
   4:     public static EnvelopeVersion None { get; }
<!--CRLF-->
   5:     public static EnvelopeVersion Soap11 { get; }
<!--CRLF-->
   6:     public static EnvelopeVersion Soap12 { get; }
<!--CRLF-->
   7: }
<!--CRLF-->
   1: public sealed class AddressingVersion
<!--CRLF-->
   2: {   
<!--CRLF-->
   3:     //其他成员      
<!--CRLF-->
   4:     public static AddressingVersion None { get; }    
<!--CRLF-->
   5:     public static AddressingVersion WSAddressing10 { get; }
<!--CRLF-->
   6:     public static AddressingVersion WSAddressingAugust2004 { get; }
<!--CRLF-->
   7: }
<!--CRLF-->

注: MessageVersion的静态方法CreateVersion(EnvelopeVersion envelopeVersion)默认采用的AddressingVersion为WSAddressing10。

由于EnvelopeVersion和AddressingVersion共同决定了MessageVesion。所以EnvelopeVersion和AddressingVersion的两两组合就得到相应的MessageVersion。这些通过两者组合得到的MessageVersion通过静态只读属性定义在MessageVersion类中。Soap11WSAddressing10、Soap11WSAddressingAugust2004、Soap12WSAddressing10和Soap12WSAddressingAugust2004的含义都是一目了然的,而None、Soap11和Soap12表示的EnvelopeVersion和Addressing组合分别是:

  • None:EnvelopeVersion.None + AddressingVersion.None;
  • Soap11:EnvelopeVersion.Soap11+ AddressingVersion.None;
  • Soap12:EnvelopeVersion.Soap12 + AddressingVersion.None
   1: public sealed class MessageVersion
<!--CRLF-->
   2: {   
<!--CRLF-->
   3:     //其他成员      
<!--CRLF-->
   4:     public static MessageVersion Default { get; }
<!--CRLF-->
   5: 
<!--CRLF-->
   6:     public static MessageVersion None { get; }
<!--CRLF-->
   7:     public static MessageVersion Soap11 { get; }
<!--CRLF-->
   8:     public static MessageVersion Soap11WSAddressing10 { get; }
<!--CRLF-->
   9:     public static MessageVersion Soap11WSAddressingAugust2004 { get; }
<!--CRLF-->
  10:     public static MessageVersion Soap12 { get; }
<!--CRLF-->
  11:     public static MessageVersion Soap12WSAddressing10 { get; }
<!--CRLF-->
  12:     public static MessageVersion Soap12WSAddressingAugust2004 { get; }
<!--CRLF-->
  13: }
<!--CRLF-->

WS-Addressing是建立在SOAP之上的,所以EnvelopeVersion.None和AddressingVersion.WSAddressingAugust2004与AddressingVersion.WSAddressing10的组合是无效的。此外在MessageVersion中还定义了一个静态只读属性Default,表示默认的MessageVersion,目前该值为MessageVersion.Soap12WSAddressing10。

二、如何创建消息

由于Message是一个抽象类型,不能直接实例化。Message类中定义了一系列静态CreateMessage方法,使我们能够方便快捷地以不同的方式进行消息的创建。对于如此众多的CreateMessage方法,按照具体的消息创建方式的不同,大体上可以分为5类:

  • 创建空消息;
  • 将对象序列化成消息的主体(Body);
  • 通过XMLWriter将内容“写”到消息中;
  • 通过XMLReader将内容“读”到消息中;
  • 创建Fault消息。

1、创建空消息

下面是所有CreateMessage静态方法中最简单的一个,包含两个输入参数:消息的版本和Action。通过该方法可以创建一个只包含Action报头的SOAP消息。

   1: public abstract class Message : IDisposable
<!--CRLF-->
   2: {
<!--CRLF-->
   3:     //其他成员
<!--CRLF-->
   4:     public static Message CreateMessage(MessageVersion version, string action);    
<!--CRLF-->
   5: }
<!--CRLF-->

为演示消息的创建以及创建后的消息的结构,我写了下面一个辅助方法WriteMessage。该方法将一个Message对象写入一个文件中,并通过开启进程的方式将文件打开。

   1: static void WriteMessage(Message message, string fileName)
<!--CRLF-->
   2: {
<!--CRLF-->
   3:     using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))
<!--CRLF-->
   4:     {
<!--CRLF-->
   5:         message.WriteMessage(writer);
<!--CRLF-->
   6:     }
<!--CRLF-->
   7:     Process.Start(fileName);
<!--CRLF-->
   8: }
<!--CRLF-->

通过下面的代码,调用Message的CreateMessage方法,并设置消息版本为MessageVersion.Soap12WSAddressing10,Action设置为http://www.artech.com/myaction。最终将会生成如后面XML片断所示的SOAP消息。

   1: string fileName = @"E:\message.xml";
<!--CRLF-->
   2: Message message = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "http://www.artech.com/myaction");
<!--CRLF-->
   3: WriteMessage(message, fileName); 
<!--CRLF-->
   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<!--CRLF-->
   2:     <s:Header>
<!--CRLF-->
   3:         <a:Action s:mustUnderstand="1">http://www.artech.com/myaction</a:Action>
<!--CRLF-->
   4:     </s:Header>
<!--CRLF-->
   5:     <s:Body />
<!--CRLF-->
   6: </s:Envelope> 
<!--CRLF-->

由于消息报头(Header)仅仅限于SOAP消息,所以如果将消息的版本改成MessageVersion.None,制定的Action不会被包含在消息中。实际上创建的Message对象不包含任何内容,最终生成的XML文件也不会包含任何文本信息。

   1: string fileName = @"E:\message.xml";
<!--CRLF-->
   2: Message message = Message.CreateMessage(MessageVersion.None, "http://www.artech.com/myaction");
<!--CRLF-->
   3: WriteMessage(message, fileName); 
<!--CRLF-->

2、将对象序列化成消息的主体

现在我们来关注Message的第2个重载的CreateMessage静态方法。如下面代码所示,该方法在上面一个重载方法的基础上加了一个object类型的body参数,表示消息的主体(Body)。在执行该方法的时候,相应的序列化器会被调用,将对象序列化成XML并将其置于消息的主体部分。默认的序列化器就是我们在前面介绍的DataContractSerializer。

   1: public abstract class Message : IDisposable
<!--CRLF-->
   2: {
<!--CRLF-->
   3:     //其他成员
<!--CRLF-->
   4:     public static Message CreateMessage(MessageVersion version, string action, object body);
<!--CRLF-->
   5: }
<!--CRLF-->

为了演示对象的序列化,我定义了下面一个数据契约Order,并定义了4个数据成员:OrderNo、OrderDate、Customer和ShipAddress。

   1: [DataContract(Namespace = "http://www.artech.com")]
<!--CRLF-->
   2: public class Order
<!--CRLF-->
   3: {
<!--CRLF-->
   4:     [DataMember(Name = "OrderNo", Order = 1)]
<!--CRLF-->
   5:     public Guid ID
<!--CRLF-->
   6:     { get; set; }
<!--CRLF-->
   7: 
<!--CRLF-->
   8:     [DataMember(Name = "OrderDate", Order = 2)]
<!--CRLF-->
   9:     public DateTime Date
<!--CRLF-->
  10:     { get; set; }
<!--CRLF-->
  11: 
<!--CRLF-->
  12:     [DataMember(Order = 3)]
<!--CRLF-->
  13:     public string Customer
<!--CRLF-->
  14:     { get; set; }
<!--CRLF-->
  15: 
<!--CRLF-->
  16:     [DataMember(Order = 4)]
<!--CRLF-->
  17:     public string ShipAddress
<!--CRLF-->
  18:     { get; set; }
<!--CRLF-->
  19: }
<!--CRLF-->

通过下面的代码,创建Order对象,并将其传入CreateMessage方法,作为body参数。最终将会生成如后面所示的SOAP消息。

   1: string fileName = @"E:\message.xml";
<!--CRLF-->
   2: Order order = new Order
<!--CRLF-->
   3: {
<!--CRLF-->
   4:     ID = Guid.NewGuid(),
<!--CRLF-->
   5:     Date = DateTime.Today,
<!--CRLF-->
   6:     Customer = "Foo",
<!--CRLF-->
   7:     ShipAddress = "#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province"
<!--CRLF-->
   8: };
<!--CRLF-->
   9: Message message = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "http://www.artech.com/myaction", order);
<!--CRLF-->
  10: WriteMessage(message, fileName);
<!--CRLF-->
   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<!--CRLF-->
   2:     <s:Header>
<!--CRLF-->
   3:         <a:Action s:mustUnderstand="1">http://www.artech.com/myaction</a:Action>
<!--CRLF-->
   4:     </s:Header>
<!--CRLF-->
   5:     <s:Body>
<!--CRLF-->
   6:         <Order xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.artech.com">
<!--CRLF-->
   7:             <OrderNo>104a0213-1a0b-4d0b-b084-e912a991f908</OrderNo>
<!--CRLF-->
   8:             <OrderDate>2008-12-17T00:00:00+08:00</OrderDate>
<!--CRLF-->
   9:             <Customer>Foo</Customer>
<!--CRLF-->
  10:             <ShipAddress>#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</ShipAddress>
<!--CRLF-->
  11:         </Order>
<!--CRLF-->
  12:     </s:Body>
<!--CRLF-->
  13: </s:Envelope>
<!--CRLF-->

从上面生成的XML,我们可以看出SOAP的主体部分就是Order对象通过DataContractSerializer序列化生成的XML。如果我们的消息不是一个SOAP消息呢?为了演示非SOAP消息的创建,我们将消息的版本替换成MessageVersion.None。从最终产生的XML结构来看,消息的整个部分就是Order对象序列化后的XML。

   1: //其他代码
<!--CRLF-->
   2: Message message = Message.CreateMessage(MessageVersion.None, "http://www.artech.com/myaction", order);
<!--CRLF-->
border-style: none; margin: 0em; padding: 0px; overflow: visible; text-align: left;

  


  
分享到:
评论

相关推荐

    WCF技术剖析,wcf教程+.pdf

    蒋金楠老师的wcf教程,个人看了,觉得写的很不错,对刚入门和深入了解wcf都适用,有需要的同学可下载学习

    WCF技术剖析pdf

    WCF技术剖析

    WCF技术剖析(卷1)

    WCF技术剖析(卷1),是一本非常专业的不错的WCF技术类的书籍。

    Apress Pro WCF 4

    Apress Pro WCF 4 Practical Microsoft SOA Implementation, 2nd Part I: Introducing Windows Communication Foundation ■Chapter 1: WCF and SOA Basics ■Chapter 2: What’s New in WCF 4 ■Chapter 3: ...

    WCF技术剖析.pdf

    深入剖析消息编码则会帮助读者从根本上把握WCF进行消息编码和解码的实现机制,以及不同编码方式在性能、互操作性及使用场景上的差异;同时本书从WCF的服务端框架和客户端框架进行深层次的剖析,介绍了服务寄宿和服务...

    WCF技术专题:WCF入门与进阶

    讲解WCF技术的,适合各种阶段的学习。从入门到进阶。

    WCF 技术剖析 +源码

    蒋金楠 作品+原书加源码,非常适合学习,剩下你懂的。

    WCF技术剖析 PDF格式文档

    WCF技术剖析 WCF技术剖析PDF版 清晰 蒋金楠著作

    WCF技术剖析 卷1

    WCF技术剖析 卷1

    WCF技术专题_第五讲WCF通信模式

    常用的分布式技术有COM+、.NET远程技术(Remoting)、Web Service和微软消息队列服务,WCF技术将这些分布式技术整合为一个高效的API。常用的那些分布式技术只能解决项目开发中某个方面的问题,并且不同技术对平台...

    WCF全面解析(套装上下册) 高清pdf 带书签 part2(共4)

    《WCF全面解析(套装上下册)》由蒋金楠所著,是作者多年潜心研究WCF技术的心血之作,也是这些年来从事WCF开发的经验总结。书如其名,此书涵盖了WCF几乎所有的知识点,并对其底层框架进行了“庖丁解牛”式的剖析,力求...

    WCF技术专题_第一讲WCF的优势及特性

    常用的分布式技术有COM+、.NET远程技术(Remoting)、Web Service和微软消息队列服务,WCF技术将这些分布式技术整合为一个高效的API。常用的那些分布式技术只能解决项目开发中某个方面的问题,并且不同技术对平台...

    WCF技术剖析(卷1)(带目录)

    WCF技术剖析(卷1)(带目录)

    WCF技术剖析

    WCF技术剖析 从基础开始:WCF简介,终结点地址和wcf寻址等

    我的WCF之旅:计算器 WCF样例源代码

    我的WCF之旅- 创建一个简单的WCF程序 - Artech WCF入门之选绝佳的例子 代源源于:《WCF全面解析 上》 编程工具:VS2010 语言:C# blog 《IIS站点中部署 WCF项目》

    WCF技术剖析 卷1 完整版

    WCF技术剖析 卷1 完整章节 完整书签

    WCF技术剖析(卷1)中文版 与相关的博客文章

    WCF技术剖析(卷1)只含5——7章 其它章相关技术在相关博客文章中有提到 相关资源见:Programming WCF Services 与对应中文版:WCF编程第二版(中文版)

Global site tag (gtag.js) - Google Analytics