Welcome

首页 / 软件开发 / WCF / WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化

WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化2012-11-07 博客园 Artech在本篇文章中,我们将讨论WCF四大契约(服务契约、数据契约、消息契约和错误契约)之一的消息契约(Message Contract)。服务契约关注于对服务操作的描述,数据契约关注于对于数据结构和格式的描述,而消息契约关注的是类型成员与消息元素的匹配关系。

我们知道只有可序列化的对象才能通过服务调用在客户端和服务端之间进行传递。到目前为止,我们知道的可序列化类型有两种:一种是应用了System.SerializableAttribute特性或者实现了System.Runtime.Serialization.ISerializable接口的类型;另一种是数据契约对象。对于基于这两种类型的服务操作,客户端通过System.ServiceModel.Dispatcher.IClientMessageFormatter将输入参数格式化成请求消息,输入参数全部内容作为有效负载置于消息的主体中;同样地,服务操作的执行结果被System.ServiceModel.Dispatcher.IDispatchMessageFormatter序列化后作为回复消息的主体。

在一些情况下,具有这样的要求:当序列化一个对象并生成消息的时候,希望将部分数据成员作为SOAP的报头,部分作为消息的主体。比如说,我们有一个服务操作采用流的方式进行文件的上载,除了以流的方式传输以二进制表示的文件内容外,还需要传输一个额外的基于文件属性的信息,比如文件格式、文件大小等。一般的做法是将传输文件内容的流作为SOAP的主体,将其属性内容作为SOAP的报头进行传递。这样的功能,可以通过定义消息契约来实现。

一、消息契约的定义

消息契约和数据契约一样,都是定义在数据(而不是功能)类型上。不过数据契约旨在定义数据的结构(将数据类型与XSD进行匹配),而消息契约则更多地关注于数据的成员具体在SOAP消息中的表示。消息契约通过以下3个特性进行定义:System.ServiceModel.MessageContractAttribute、System.ServiceModel.MessageHeaderAttribute、System.ServiceModel.MessageBodyMemberAttribute。MessageContractAttribute应用于类型上,MessageHeaderAttribute和MessageBodyMemberAttribute则应用于属性或者字段成员上,表明相应的数据成员是一个基于SOAP报头的成员还是SOAP主体的成员。先来简单介绍一下这3个特性:

1、MessageContractAttribute

通过在一个类或者结构(Struct)上应用MessageContractAttribute使之成为一个消息契约。从MessageContractAttribute的定义来看,MessageContractAttribute大体上具有以下两种类型的属性成员:

ProtectionLevel和HasProtectionLevel:表示保护级别,在服务契约中已经对保护级别作了简单的介绍,WCF中通过System.Net.Security.ProtectionLevel枚举定义消息的保护级别。一般有3种可选的保护级别:None、Sign和EncryptAndSign

IsWrapped、WrapperName、WrapperNamespace:IsWrapped表述的含义是是否为定义的主体成员(一个或者多个)添加一个额外的根节点。WrapperName和WrapperNamespace则表述该根节点的名称和命名空间。IsWrapped、WrapperName、WrapperNamespace的默认是分别为true、类型名称和http://tempuri.org/。

 1: [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = false)]
2: public sealed class MessageContractAttribute : Attribute
3: {
4: //其他成员
5: public bool HasProtectionLevel { get; }
6: public ProtectionLevelProtectionLevel { get; set; }
7:
8: public bool IsWrapped { get; set; }
9: public string WrapperName { get; set; }
10: public string WrapperNamespace { get; set; }
11: }
下面的代码中将Customer类型通过应用MessageContractAttribute使之成为一个消息契约。ID和Name属性通过应用MessageHeaderAttribute定义成消息报头(Header)成员,而Address属性则通过MessageBodyMemberAttribute定义成消息主体(Body)成员。后面的XML体现的是Customer对象在SOAP消息中的表现形式。

 1: [MessageContract]
2: public class Customer
3: {
4: [MessageHeader(Name = "CustomerNo", Namespace = "http://www.artech.com/")]
5: public Guid ID
6: { get; set; }
7:
8: [MessageHeader(Name = "CustomerName", Namespace = "http://www.artech.com/")]
9: public string Name
10: { get; set; }
11:
12: [MessageBodyMember(Namespace = "http://www.artech.com/")]
13: public string Address
14: { get; set; }
15: }
1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
2: <s:Header>
3: <a:Action s:mustUnderstand="1">http://tempuri.org/IOrderManager/ProcessOrder</a:Action>
4: <h:CustomerName xmlns:h="http://www.artech.com/">Foo</h:CustomerName>
5: <h:CustomerNo xmlns:h="http://www.artech.com/">2f62405b-a472-4d1c-8c03-b888f9bd0df9</h:CustomerNo>
6: </s:Header>
7: <s:Body>
8: <Customer xmlns="http://tempuri.org/">
9: <Address xmlns="http://www.artech.com/">#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>
10: </Customer>
11: </s:Body>
12: </s:Envelope>
如果我们将IsWrapped的属性设为false,那么套在Address节点外的Customer节点将会从SOAP消息中去除。

 1: [MessageContract(IsWrapped = false)]
2: public class Customer
3: {
4: //省略成员
5: }
1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
2: ......
3: <s:Body>
4: <Address xmlns="http://www.artech.com/">#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>
5: </s:Body>
6: </s:Envelope>
我们同样可以自定义这个主体封套(Wrapper)的命名和命名空间。下面我们就通过将MessageContractAttribute的WrapperName和WrapperNamespace属性设为Cust和http://www.artech.com/。

 1: [MessageContract(IsWrapped = true, WrapperName = "Cust", WrapperNamespace = "http://www.artech.com/")]
2: public class Customer
3: {
4: //省略成员
5: }
1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
2: ......
3: <s:Body>
4: <Cust xmlns="http://www.artech.com/">
5: <Address>#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>
6: </Cust>
7: </s:Body>
8: </s:Envelope>