WCF後傳系列(10):消息處理功能核心

概述

WCF是一個通訊框架,同時也能夠將它當作是一個消息處理或者傳遞的基礎框架,它能夠接收消息、對消息作處理,或者根據客戶端給定的數據構造消息並將消息發送到目標端點,在這個過程當中,一切都是圍繞「消息」而展開的。WCF在消息處理體系結構提供統一編程模型的同時,還容許靈活的表示數據和傳遞消息,本文將介紹如何配置消息支持各個SOAP和WS-Addressing版本或者不用任何SOAP和WS-Addressing,以及如何控制消息狀態等。

消息契約

在大多數狀況下,開發者只關心數據契約而沒必要考慮攜帶這些數據的消息,然而某些特殊狀況下,須要徹底控制SOAP消息的結構,如提供戶操做性,或者控制消息的某一部分的安全性,此時可使用WCF中提供的編程模型消息契約,它使用一種可直接序列化爲所需精確SOAP消息的類型。若是爲某一個數據類型定義了消息契約,咱們能夠徹底控制該類型和SOAP消息之間的映射,以下面的代碼:
[MessageContract]
public class CustomerMessage
{
    [MessageHeader]
    public Guid Id { get; set; }
    [MessageBodyMember]
    public String Name { get; set; }
    [MessageBodyMember]
    public String Email { get; set; }
}
此處使用MessageContract特性指定CustomerMessage類型爲消息契約,並用MessageHeader指定Id屬性在SOAP消息的標頭,用MessageBodyMember指定Name、Email做爲SOAP消息的正文,若是攔截到SOAP消息,能夠看到以下所示:
<s:Envelope xmlns:s="[url]http://schemas.xmlsoap.org/soap/envelope/[/url]">
  <s:Header>
    <Action s:mustUnderstand="1"
      xmlns="[url]http://schemas.microsoft.com/ws/2005/05/addressing/none[/url]">
      [url]http://tempuri.org/ICustomerContract/GetCustomerResponse[/url]
    </Action>
    <h:Id xmlns:h="[url]http://tempuri.org/[/url]">
      38097c1d-366d-4c58-84a5-93525766630c
    </h:Id>
  </s:Header>
  <s:Body>
    <CustomerMessage xmlns="[url]http://tempuri.org/[/url]">
      <Email>lhj_cauc[@@AT@@]163.com</Email>
      <Name>TerryLee</Name>
    </CustomerMessage>
  </s:Body>
</s:Envelope>
固然,還能夠在消息契約中使用數組,對消息的部分進行簽名和加密等操做以及指定標頭和正文部分的命名空間,這些不是本文的重點,將再也不闡述。能夠看到,消息契約爲開發者徹底控制SOAP消息和自定義類型之間的映射,提供了一種很是方便的途徑。

認識Message類型

絕大多數狀況下,咱們都不會直接去使用Message類,而是僅僅使用WCF服務編程模型中的數據契約、消息契約來描述輸入或者輸出消息。但在某些高級應用中,咱們須要對Message類進行編程,如須要從別處建立輸出消息的內容,而不是序列化.NET Framework類型,如可能從磁盤上的某個文件來建立輸出消息,在這種狀況下,簡單的使用WCF中服務編程模型已經不能知足須要,而須要針對Message類進行編程。
簡單來講,Message類是一個通用的數據容器,在本質上它徹底模擬SOAP消息正文以及消息標頭和屬性的集合,另外Message類中提供了一系列的方法用來建立消息、讀寫消息正文以及標頭和屬性的集合。它的定義以下所示:
public abstract class Message : IDisposable
{
    // 標頭集合
    public abstract MessageHeaders Headers { get; }
    protected bool IsDisposed { get; }
    public virtual bool IsEmpty { get; }
    public virtual bool IsFault { get; }
    // 屬性集合
    public abstract MessageProperties Properties { get; }
    public MessageState State { get; }
    // 消息版本
    public abstract MessageVersion Version { get; }
    public void Close();
    public MessageBuffer CreateBufferedCopy(int maxBufferSize);
    public static Message CreateMessage(MessageVersion version, string action);
    // 獲取正文
    public T GetBody<T>();
    public void WriteBody(XmlWriter writer);
    public void WriteMessage(XmlWriter writer);
    public void WriteStartBody(XmlWriter writer);
    public void WriteStartEnvelope(XmlDictionaryWriter writer);
    // 更多成員
}

消息版本

在建立消息時,一個很重要的部分就是指定消息版本,消息版本標識了消息在傳輸中使用SOAP和WS-Addressing的版本,即每一個消息版本都會由兩部分組成,SOAP版本和WS-Addressing版本。在WCF中,支持的SOAP信封版本用EnvelopeVersion類來表示,以下代碼:
public sealed class EnvelopeVersion
{
    public static EnvelopeVersion None { get; }
    public static EnvelopeVersion Soap11 { get; }
    public static EnvelopeVersion Soap12 { get; }
}
None表示在傳輸中不適用SOAP,即便用傳統的XML消息傳遞方案;Soap11和Soap12分別表示在傳輸中使用SOAP 1.1版本和SOAP 1.2版本。
一樣,在WCF中支持的WS-Addressing版本用AddressingVersion類來表示,以下代碼:
public sealed class AddressingVersion
{
    public static AddressingVersion None { get; }
    public static AddressingVersion WSAddressing10 { get; }
    public static AddressingVersion WSAddressingAugust2004 { get; }
}
WSAddressingAugust2004表示2004年8月開始提交的WS-Addressing W3C提案,目前獲得普遍支持。而WSAddressing10表示最終的WS-Addressing 1.0 W3C推薦標準。
在建立消息時,咱們須要指定消息版本,這個消息版本包括所使用的SOAP和WS-Addressing規範的版本,以下代碼所示:
MessageVersion version = MessageVersion.CreateVersion
    (EnvelopeVersion.Soap11, AddressingVersion.WSAddressing10);
此外,在MessageVersion中,已經提供了對這二者之間的常見組合,這樣咱們可使用而不用考慮二者之間的兼容性等問題,以下代碼所示:
public sealed class MessageVersion
{
    public static MessageVersion Default { get; }
    public static MessageVersion None { get; }
    public static MessageVersion Soap11 { get; }
    public static MessageVersion Soap11WSAddressing10 { get; }
    public static MessageVersion Soap11WSAddressingAugust2004 { get; }
    public static MessageVersion Soap12 { get; }
    public static MessageVersion Soap12WSAddressing10 { get; }
    public static MessageVersion Soap12WSAddressingAugust2004 { get; }
}
咱們看看在SOAP消息中,SOAP版本和WS-Addressing版本是如何體現的,建立一個簡單的消息,使用的消息版本爲Soap11WSAddressing10:
public Message GetCustomer()
{
    Customer customer = new Customer
    {
        Id = Guid.NewGuid(),
        Name = "TerryLee",
        Email = "lhj_cauc[@@AT@@]163.com"
    };
    Message message = Message.CreateMessage(
        MessageVersion.Soap11WSAddressing10,
        "http://localhost/CustomerService/GetCustomer",
        customer);
    return message;
}
能夠看到SOAP消息包爲以下所示,從命名空間上能夠看到使用的SOAP版本爲1.1而WS-Addressing爲1.0。
<s:Envelope xmlns:a="[url]http://www.w3.org/2005/08/addressing[/url]"
            xmlns:s="[url]http://schemas.xmlsoap.org/soap/envelope/[/url]">
  <s:Header>
    <a:Action s:mustUnderstand="1">
      [url]http://localhost/CustomerService/GetCustomer[/url]
    </a:Action>
  </s:Header>
  <s:Body>
    <Customer xmlns:i="[url]http://www.w3.org/2001/XMLSchema-instance[/url]"
              xmlns="[url]http://schemas.datacontract.org/2004/07/[/url] Data">
      <Email>lhj_cauc[@@AT@@]163.com</Email>
      <Id>d297aa45-2d9e-4f89-aa41-491507db2a21</Id>
      <Name>TerryLee</Name>
    </Customer>
  </s:Body>
</s:Envelope>
若是修改消息的版本爲Soap12WSAddressingAugust2004,能夠看到它們命名空間發生的變化:
<s:Envelope xmlns:a="[url]http://schemas.xmlsoap.org/ws/2004/08/addressing[/url]"
            xmlns:s="[url]http://www.w3.org/2003/05/soap-envelope[/url]">
  <s:Header>
    <a:Action s:mustUnderstand="1">
      [url]http://localhost/CustomerService/GetCustomer[/url]
    </a:Action>
  </s:Header>
  <s:Body>
    <Customer xmlns:i="[url]http://www.w3.org/2001/XMLSchema-instance[/url]"
              xmlns="[url]http://schemas.datacontract.org/2004/07/[/url] Data">
      <Email>lhj_cauc[@@AT@@]163.com</Email>
      <Id>e13bef92-bba2-47c2-954c-ba7bfe472cc2</Id>
      <Name>TerryLee</Name>
    </Customer>
  </s:Body>
</s:Envelope>

終結點配置與消息

你們都知道終結點的配置由契約、地址和綁定組成,其中契約定義了消息和方法之間的映射,而地址則指定了服務在何處,在綁定中描述了所要使用的傳輸,消息採用的編碼方法以及支持WS-*系列協議,同時還有消息版本,對於每一個綁定來講,它所使用的消息編碼器和消息版本不盡相同,關於消息編碼器將會在後面詳細講述,先來看一下消息版本,如在服務端有以下配置:
<endpoint address=""
          binding ="basicHttpBinding"
          contract="TerryLee.MessageHandling.Contract.ICustomerContract"
          name="defaultBinding">
</endpoint>
<endpoint address="Other"
          binding ="wsHttpBinding"
          contract="TerryLee.MessageHandling.Contract.ICustomerContract"
          name="otherBinding">
</endpoint>
咱們在宿主端分別輸出一下它們所採用的消息版本:
foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
{
    Console.WriteLine("Binding:{0}", endpoint.Binding.Name);
    Console.WriteLine("AddressingVersion:{0}", 
        endpoint.Binding.MessageVersion.Addressing.ToString());
    Console.WriteLine("EnvelopeVersion:{0}",
        endpoint.Binding.MessageVersion.Envelope.ToString());
    Console.WriteLine("----------------------------\n");
}
最後結果以下:
Binding:BasicHttpBinding
AddressingVersion:AddressingNone ([url]http://schemas.microsoft.com/ws/2005/05/addres[/url]
sing/none)
EnvelopeVersion:Soap11 ([url]http://schemas.xmlsoap.org/soap/envelope/[/url])
----------------------------
Binding:WSHttpBinding
AddressingVersion:Addressing10 ([url]http://www.w3.org/2005/08/addressing[/url])
EnvelopeVersion:Soap12 ([url]http://www.w3.org/2003/05/soap-envelope[/url])
----------------------------
能夠看到,對於BasicHttpBinding來講,它的Addressing版本爲None,SOAP版本爲Soap11,即MessageVersion爲Soap11;而對於WSHttpBinding來講,它的Addressing版本爲Addressing10,SOAP版本爲Soap12,即MessageVersion爲Soap12WSAddressing10。

操做消息

在建立消息時,能夠從其它文件寫入消息正文,或者把自定義類型序列化到消息正文中。一樣咱們還能夠控制消息的標頭和屬性,標頭將會在SOAP消息中進行傳輸,因此當中介在檢查標頭時,必須支持標頭使用的協議的基礎版本;而屬性提供一種與版本更加無關的方式來批註消息。以下面的代碼:
public Message GetCustomer()
{
    Customer customer = new Customer
    {
        Name = "TerryLee",
        Email = "lhj_cauc[@@AT@@]163.com"
    };
    Message message = Message.CreateMessage(
        MessageVersion.Soap12WSAddressingAugust2004,
        "http://localhost/CustomerService/GetCustomer",
        customer);
    message.Headers.Add(MessageHeader.CreateHeader(
        "CustomerID",
        "http://www.cnblogs.com/terrylee",
        Guid.NewGuid()
        ));
    return message;
}
SOAP消息以下所示,能夠看到CustomerID放在SOAP消息標頭中:
<s:Envelope xmlns:a="[url]http://schemas.xmlsoap.org/ws/2004/08/addressing[/url]"
            xmlns:s="[url]http://www.w3.org/2003/05/soap-envelope[/url]">
  <s:Header>
    <a:Action s:mustUnderstand="1">
      [url]http://localhost/CustomerService/GetCustomer[/url]
    </a:Action>
    <CustomerID xmlns="[url]http://www.cnblogs.com/terrylee[/url]">
      c2f34dd3-d71a-42fa-b3f2-6f58c553c8ee
    </CustomerID>
  </s:Header>
  <s:Body>
    <Customer xmlns:i="[url]http://www.w3.org/2001/XMLSchema-instance[/url]"
              xmlns="[url]http://schemas.datacontract.org/2004/07/Data[/url]">
      <Email>lhj_cauc[@@AT@@]163.com</Email>
      <Id>00000000-0000-0000-0000-000000000000</Id>
      <Name>TerryLee</Name>
    </Customer>
  </s:Body>
</s:Envelope>

消息狀態控制

在WCF中,Message類的正文對象已經設計爲支持流處理,這意味着在Message的生命週期內只能被處理一次。這是經過保持Message對象的當前狀態來強制實施的。當Message對象處於Created狀態時,可讀取/寫入/複製該對象,其餘狀態爲 Read、Written 和 Copied,這意味着相應的操做已經執行過一次。Message對象的五種狀態定義以下:
public enum MessageState
{
    Created,
    Read,
    Written,
    Copied,
    Closed,
}
Message對象在開始時處於Created狀態,該狀態是處理正文的惟一有效狀態。處理正文有如下幾種不一樣的方式:能夠對其進行讀取、寫入或複製。調用GetReaderAtBodyContents或 GetBody<T> 可將狀態更改成Read。調用WriteMessage或WriteBody可將狀態更改成Written。調用 CreateBufferedCopy可將狀態更改成Copied,以下圖所示:
TerryLee_WCF_34
以下面的代碼:
Customer c =  new Customer { 
    Name = "TerryLee",
    Email = "lhj_cauc[@@AT@@]163.com"
};
Message message = Message.CreateMessage(
    MessageVersion.Soap12WSAddressingAugust2004,
    "http://localhost/CustomerService/GetCustomer",
    c);
Console.WriteLine(message.State);
Customer c = message.GetBody<Customer>();
Console.WriteLine(message.State);
message.Close();
Console.WriteLine(message.State);
輸出的Message狀態分別爲:
Created
Read
Closed

小結

WCF在消息處理體系結構提供統一編程模型的同時,還容許靈活的表示數據和傳遞消息。從本文能夠看出,它能夠配置消息支持各個SOAP和WS-Addressing版本或者不適用任何SOAP和WS-Addressing,這將提供極大的靈活性。

0javascript

收藏css

lihuijun

203篇文章,74W+人氣,0粉絲

Ctrl+Enter 發佈html

發佈java

取消jquery

掃一掃,領取大禮包git

0ajax

分享
lihuijun
相關文章
相關標籤/搜索