消息契约描述了要发送或接收的SOAP消息的结构。可以检查并且控制许多SOAP消息的头和消息体的细节。鉴于数据契约可以通过标准XML结构定义语言(XSD)进行互操作,那么消息契约就可以让你通过SOAP消息和许多其他系统进行互操作。

利用消息契约可以让你通过提供的直接访问SOAP头和体的机制,完全控制SOAP消息的发送和接收。它允许使用简单或复杂的类型来定义SOAP中某部分的确切内容。正如当你需要完全控制数据序列化时,可以从DataContractSerializer切换为XmlSerializer一样,当你需要完全控制SOAP消息时,可以从DataContracts转变为MessageContracts。

如果你想从某个操作进行不同信道的消息传输,那么在SOAP头中传递消息就是必要的。例如,会话或相关信息可以在头中被传递,而不是添加附加的参数到操作或者添加消息得到数据本身。另一个例子是安全,即也许你需要实现一个自定义的安全协议(绕过WS-Security),并在自定义的SOAP消息头中传递证书。第三个例子,还是安全,即签名和加密那些你想要签名或加密的SOAP头的全部信息或其中一部分信息。这一技术的缺点是客户端和服务端必须从SOAP消息头中手动添加和检索消息,而没有序列化的数据契约和操作契约帮你完成这些事。

[MessageContract]属性定义SOAP消息的结构。这个属性没有太多的修饰,因为它的目的是定义消息的边界,而不是内容本身。唯一的修饰符涉及的是多个消息体如何包装成一个SOAP消息,指定是否包括所有。如果是,则为这个包装定义名字和命名空间。

类型化的消息使用[MessageHeader]和[MessageBodyMember]属性来描述SOAP消息头和消息体的结构。客户端和服务端之后可以使用序列化类型引用这些数据。附加信息可以与消息头关联,如名字和命名空间,以及是否能够传达消息,以及谁是最后的活动者或消息的接收者。附加信息也可以与消息体相关联,如名字和命名空间。如果有多个消息体可用,消息契约可以定义这些部分的顺序。无论头还是消息体都可以有简单或复杂的类型定义。

非类型化的消息不需要使用任何属性来描述它的内容。这完全取决于运行时的代码来识别内容。这对于直接操作XML的信息集来说是非常有用的。在这种情况下你会希望WCF可以直接用你的代码操作文档对象模型。操作非类型化消息并返回消息类型的服务端操作,实现了XML信息集。

类型化消息

下面代码显示了一个类型化信息的契约。头包含一个简单的类型——DateTime。消息体包含一个复杂类型——PriceDetails。PriceDetails类必须被序列化,并且使用[DataContract]属性或如代码所示使用[Serializable]属性。这个例子仅仅有一个消息头和消息体,但是现实中可以有多个。

如果多个消息头和消息体要通过不同层次的客户端软件上的消耗,你可能要指定多个消息头和消息体。例如,一层也许需要SOAP头相关的信息来访问一个请求的响应,而另一层可能需要身份信息来适当的路由消息。这种情况下,两个消息头有两个目的,所以没有必要把它们的结构合二为一。

请注意,服务中的操作接收和发送消息类型。当使用消息契约是,无论是输入参数还是输出参数必须也是消息并且被[MessageContract]属性标记。更具体的说,操作必须刚好包含一个输入参数并必须返回一个输出结果,而无论是输入还是输出都必须是消息,因为请求和响应的消息发送到以及来自的操作将会直接映射这些参数的SOAP表现形式。此外,基于消息的编程机制和基于参数的编程机制不能混合,所以你不能指定数据契约为输入参数,并将其返回为消息契约,或指定一个消息契约作为输入参数却返回一个数据契约。你可以混合类型化和非类型化的消息,但是不能混合数据契约和消息契约。混用消息契约和数据契约将会在服务端生成WSDL时导致运行时错误。

为了生成客户端代理代码来表示[MessageContract]所标示的类型化消息,在添加服务引用的高级对话框中,你需要选中“总是检查消息契约”选项。image_thumb1

或者,你可以使用svcutil.exe中的/messageContract,或/mc,选项。这样svcutil.exe就会生成接收类型化消息,并允许客户端调用的面向方法的公共方法。如果你在使用svcutil.exe时,没有包含/mc选项,或如果你使用添加服务引用对话框时,没有选择“总是生成消息契约”,代理将会生成只接收普通参数并且内部调用的基于消息的方法。在这两种情况下,线路上发送的XML消息是相同的。

定义一个类型化消息契约:

image_thumb11

PriceDetails类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DefiningATypedMessageContract
{
    [Serializable]
    public class PriceDetails
    {
        public string Ticker;
        public double Amount;
    }
}
返回的查询结果StockPrice类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace DefiningATypedMessageContract
{
    [MessageContract]
    public class StockPrice
    {
        [MessageHeader]
        public DateTime CurrentTime;
        [MessageBodyMember]
        public PriceDetails Price;
    }
}
传入的查询条件StockPriceReq类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace DefiningATypedMessageContract
{
    [MessageContract]
    public class StockPriceReq
    {
        [MessageBodyMember]
        public string Ticker;
    }
}
服务契约的接口IStockService:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace DefiningATypedMessageContract
{
    [ServiceContract]
    public interface IStockService
    {
        // TODO: Add your service operations here
        [OperationContract]
        StockPrice GetPrice(StockPriceReq req);
    }
}

服务契约接口的实现类StockService:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace DefiningATypedMessageContract
{
    public class StockService : IStockService
    {

        #region IStockService Members

        public StockPrice GetPrice(StockPriceReq req)
        {
            StockPrice resp = new StockPrice();
            resp.Price = new PriceDetails();
            resp.Price.Ticker = req.Ticker;
            resp.Price.Amount = 94.85;
            return resp;
        }

        #endregion
    }
}

运行解决方案如下调用:

image_thumb3

下面代码展示了从服务端返回给客户端的SOAP消息的XML形式。注意,[MessageHeader]元素——CurrentTime出现在了SOAP头中;而[MessageBodyMember]元素——Price出现在了SOAP体中。

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <s:Header>
    <a:Action s:mustUnderstand="1" u:Id="_2">http://tempuri.org/IStockService/GetPriceResponse</a:Action>
    <h:CurrentTime u:Id="_3" xmlns:h="http://tempuri.org/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">0001-01-01T00:00:00</h:CurrentTime>
    <a:RelatesTo u:Id="_5">urn:uuid:20c2b591-56a2-401a-9ddf-f5cf2b9d3bcd</a:RelatesTo>
    <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <u:Timestamp u:Id="uuid-828dfadf-95d0-497e-8996-5f84d037bae5-11">
        <u:Created>2009-11-02T07:53:57.857Z</u:Created>
        <u:Expires>2009-11-02T07:58:57.857Z</u:Expires>
      </u:Timestamp>
      <c:DerivedKeyToken u:Id="uuid-828dfadf-95d0-497e-8996-5f84d037bae5-7" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
        <o:SecurityTokenReference>
          <o:Reference URI="urn:uuid:facde5e3-64d3-4108-a35c-9ac518486273" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" />
        </o:SecurityTokenReference>
        <c:Offset>0</c:Offset>
        <c:Length>24</c:Length>
        <c:Nonce>p852F8YOk3onS1iWmr9ZfQ==</c:Nonce>
      </c:DerivedKeyToken>
      <c:DerivedKeyToken u:Id="uuid-828dfadf-95d0-497e-8996-5f84d037bae5-8" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
        <o:SecurityTokenReference>
          <o:Reference URI="urn:uuid:facde5e3-64d3-4108-a35c-9ac518486273" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" />
        </o:SecurityTokenReference>
        <c:Nonce>/aQ2tCk/IZKkgpF5NlWucg==</c:Nonce>
      </c:DerivedKeyToken>
      <e:ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">
        <e:DataReference URI="#_1" />
        <e:DataReference URI="#_4" />
        <e:DataReference URI="#_6" />
      </e:ReferenceList>
      <e:EncryptedData Id="_6" Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:e="http://www.w3.org/2001/04/xmlenc#">
        <e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
          <o:SecurityTokenReference>
            <o:Reference ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/dk" URI="#uuid-828dfadf-95d0-497e-8996-5f84d037bae5-8" />
          </o:SecurityTokenReference>
        </KeyInfo>
        <e:CipherData>
          <e:CipherValue>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</e:CipherValue>
        </e:CipherData>
      </e:EncryptedData>
    </o:Security>
  </s:Header>
  <s:Body u:Id="_0">
    <StockPrice xmlns="http://tempuri.org/">
      <Price xmlns:a="http://schemas.datacontract.org/2004/07/DefiningATypedMessageContract" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:Amount>94.85</a:Amount>
        <a:Ticker>Chinasofti</a:Ticker>
      </Price>
    </StockPrice>
  </s:Body>
</s:Envelope>

非类型化消息契约:

在某些情况下,在设计时,你可能不知道在客户端和服务端传递的消息结构。例如,需要的内容可能内建在消息本身,例如在运行时才决定的服务操作和路由等消息。或者是一个介于服务端和客户端之间的合并SOAP消息和请求的特殊的数据类型的软件(或硬件)层。在这些情况下,非类型化操作契约就非常有用。

非类型化的契约可以使客户端和服务端之间在SOAP体中传递几乎任何内容,只要这些内容可以被绑定堆栈进行编码以用来通信。消息的内容对于WSDL来说是绝对的不透明的,因为没有XSD来定义这些数据。客户端和服务端执行Message类,这个类定义在System.ServiceModel.Channels中,用来创建、读取以及写入消息。

下面代码显示了一个使用message类型作为输入和输出的操作契约。注意,消息的GetBody方法是一个通用方法来反序列化消息体为一个类型。这个方法使用一个XML读取器来读取SOAP消息的<body>元素。因为它使用一个XML读取器,<body>只能读取一次;如果你想要读取它多次,你应该使用消息的CreateBufferedCopy方法。关于SOAP活动的响应是依赖于一个与响应串联到底的请求。这种活动可以被[OperationContract]属性中的(ReplyAction=)重写。

Message类有许多方法创建、读取和写入消息的内容。客户端负责建立一个消息,然后将它发送到服务,而且这种服务负责建立消息回发。发送消息之前,内容必须防止在消息体内。可以使用CreateMessage、WriteMessage或WriteBody方法可以完成上述操作。

定义并实现一个非类型化的消息契约:

服务端操作接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;

namespace DefiningAndImplementingUntypedMessageContract
{
    [ServiceContract(Namespace = "http://DefiningAndImplementingUntypedMessageContract")]
    public interface IStockService
    {
        [OperationContract]
        Message GetPrice(Message req);
    }    
}

服务端操作实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;

namespace DefiningAndImplementingUntypedMessageContract
{
    public class StockService : IStockService
    {
        #region IStockService Members

        private Message GetPrice(Message req)
        {
            string ticker = req.GetBody<string>();
            Message resp = Message.CreateMessage(req.Version,req.Headers.Action + "Response",ticker + "|" + "94.85");
            return resp;
        }

        #endregion
    }
}

客户端代码类似于服务端代码,使用CreageMessage方法来创建据欧正确版本的消息以匹配绑定信息,然后使用GetBody方法来读取服务端返回的结果。注意,CreateMessage方法采用3个参数:版本、活动和消息字符串。当创建消息时,消息的版本必须能够与用于服务端通信的绑定消息相匹配,如在管道上定义的消息版本属性。在这个例子中,活动:http://DefiningAndImplementingUntypedMessageContract/IStockService/GetPrice被SOAP和WCF的底层架构用来路由消息到服务端的目标操作。下面代码显示了与服务端通信的代码。

客户端使用非类型化消息契约发起通信:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace ClientInitiatingCommunicationUsingAnUntypedMessage
{
    class Program
    {
        static void Main(string[] args)
        {
            using(StockServiceReference.StockServiceClient proxy = 
                new ClientInitiatingCommunicationUsingAnUntypedMessage.StockServiceReference.StockServiceClient())
            {
                new OperationContextScope(proxy.InnerChannel);
                Message msgReq = Message.CreateMessage(OperationContext.Current.OutgoingMessageHeaders.MessageVersion
                    ,"http://DefiningAndImplementingUntypedMessageContract/IStockService/GetPrice"
                    ,"Chinasofti");
                Message msgResp = proxy.GetPrice(msgReq);
                Console.WriteLine("Returned {0}",msgResp.GetBody<string>());

                Console.WriteLine("Press any key to continue...");
                Console.ReadLine();
            }
        }
    }
}

下面代码显示了SOAP消息从服务端回发到客户端的响应。注意,SOAP头中的活动有响应串联到底,并且消息体中是一个XML格式的字符串。

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <s:Header>
    <a:Action s:mustUnderstand="1" u:Id="_2">http://DefiningAndImplementingUntypedMessageContract/IStockService/GetPriceResponse</a:Action>
    <a:RelatesTo u:Id="_3">urn:uuid:e975138b-c103-4f72-95a1-947088d1c1a1</a:RelatesTo>
    <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <u:Timestamp u:Id="uuid-5602f0f6-c23c-4ea9-96fd-f5f84318e69d-11">
        <u:Created>2009-11-03T02:46:47.065Z</u:Created>
        <u:Expires>2009-11-03T02:51:47.065Z</u:Expires>
      </u:Timestamp>
      <c:DerivedKeyToken u:Id="uuid-5602f0f6-c23c-4ea9-96fd-f5f84318e69d-7" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
        <o:SecurityTokenReference>
          <o:Reference URI="urn:uuid:9b38d073-7b61-45c9-8b73-6a0f5e63ec72" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" />
        </o:SecurityTokenReference>
        <c:Offset>0</c:Offset>
        <c:Length>24</c:Length>
        <c:Nonce>c+8T7TR5rteQZa+YprLgbA==</c:Nonce>
      </c:DerivedKeyToken>
      <c:DerivedKeyToken u:Id="uuid-5602f0f6-c23c-4ea9-96fd-f5f84318e69d-8" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
        <o:SecurityTokenReference>
          <o:Reference URI="urn:uuid:9b38d073-7b61-45c9-8b73-6a0f5e63ec72" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" />
        </o:SecurityTokenReference>
        <c:Nonce>Bg/Yjo6TPDJBORH4Ukpy+Q==</c:Nonce>
      </c:DerivedKeyToken>
      <e:ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">
        <e:DataReference URI="#_1" />
        <e:DataReference URI="#_4" />
      </e:ReferenceList>
      <e:EncryptedData Id="_4" Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:e="http://www.w3.org/2001/04/xmlenc#">
        <e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
          <o:SecurityTokenReference>
            <o:Reference ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/dk" URI="#uuid-5602f0f6-c23c-4ea9-96fd-f5f84318e69d-8" />
          </o:SecurityTokenReference>
        </KeyInfo>
        <e:CipherData>
          <e:CipherValue>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</e:CipherValue>
        </e:CipherData>
      </e:EncryptedData>
    </o:Security>
  </s:Header>
  <s:Body u:Id="_0">
    <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Chinasofti|94.85</string>
  </s:Body>
</s:Envelope>

使用带有非类型化消息契约的SOAP头

无论你正在使用类型化的还是非类型化的消息,你可能都希望通过除SOAP体外的SOAP头传递信息。通常的需求如伴随消息传递会话状态或上下文信息。因此,与其创建额外的封装消息,不如选择方便而且便于理解的SOAP头机制来传递信息。

如果你使用类型化消息,WCF通过[MessageHeader]属性明确支持这种操作,如上面部分介绍的代码所示。如果你使用的是非类型化消息,那么你需要显示添加一个非类型化消息头。

下面代码显示了一个实现一个非类型化消息操作的服务契约以及从消息头中读取数据。注意,消息头中的数据——timeZone,是哪行代码获得的。

访问一个非类型化消息契约消息头的服务:

服务契约接口定义:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;

namespace ServiceAccessingMessageHeadersWithAUntypedMessage
{
    [ServiceContract]
    public interface IStockService
    {
        [OperationContract]
        Message GetPrice(Message req);
    }
}

服务契约接口实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;

namespace ServiceAccessingMessageHeadersWithAUntypedMessage
{
    public class StockService : IStockService
    {
        #region IStockService Members

        Message IStockService.GetPrice(Message req)
        {
            string timeZone = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>("TimeZone"
                , "http://ServiceAccessingMessageHeadersWithAUntypedMessage");
            string ticker = req.GetBody<string>();
            Message resp = Message.CreateMessage(req.Version
                ,req.Headers.Action + "Response"
                ,timeZone + "|" + ticker + "|" + "94.85");
            return resp;
        }

        #endregion
    }
}

下面代码演示了发送消息到服务端同时,如何添加一个SOAP消息头到一个非类型化消息中。首先CreateMessage创建一个message对象,然后通过构造函数将数据放置到消息中。接着,创建一个类型化的MessageHeader;这个例子中,它是字符串类型,然后通过构造函数将数据放置到头中。下一步,从类型化的消息创建一个非类型化的MessageHeader。最后,这个非类型化的MessageHeader会被添加到消息中并发送到服务端。

客户端插入一个非类型化的消息到消息头:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace ClientInsertingMessageHeadersIntoAUntypedMessage
{
    class Program
    {
        static void Main(string[] args)
        {
            using (StockServiceReference.StockServiceClient proxy = 
                new ClientInsertingMessageHeadersIntoAUntypedMessage.StockServiceReference.StockServiceClient())
            {
                new OperationContextScope(proxy.InnerChannel);
                Message msgReq = Message.CreateMessage(OperationContext.Current.OutgoingMessageHeaders.MessageVersion
                    , "http://tempuri.org/IStockService/GetPrice"
                    ,"Chinasofti");
                MessageHeader<string> msgHeader = new MessageHeader<string>("GMT +08:00");
                MessageHeader untypedHeader = msgHeader.GetUntypedHeader("TimeZone"
                    , "http://ServiceAccessingMessageHeadersWithAUntypedMessage/");
                msgReq.Headers.Add(untypedHeader);
                Message msgResp = proxy.GetPrice(msgReq);
            }
        }
    }
}

下面代码显示了客户端生成的SOAP消息格式。注意,TimeZone元素被加入到了消息头的制定命名空间中。

<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://ServiceAccessingMessageHeadersWithAUntypedMessage/IStockService/GetPrice</a:Action>
    <TimeZone xmlns="http://ServiceAccessingMessageHeadersWithAUntypedMessage/">GMT +08:00</TimeZone>
  </s:Header>
  <s:Body>
    <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Chinasofti</string>
  </s:Body>
</s:Envelope>
posted on 2010-01-13 13:47  zhangdong  阅读(1821)  评论(0编辑  收藏  举报