程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 《WCF技術內幕》27:第2部分_第5章_消息:使用消息頭(上)

《WCF技術內幕》27:第2部分_第5章_消息:使用消息頭(上)

編輯:關於.NET

使用消息頭

正如你在第二章裡看到的一樣,消息頭塊被SOAP消息基礎結構用來表示地址 、路由和安全信息。因為WCF也是一個完全支持SOAP的消息處理基礎結構,它包 含一些創建、序列化和分析SOAP消息頭塊的工具。記住Message類型是一個 SOAP 消息的CLR抽象,它定義的成員允許WCF基礎結構使用發送或接受到的消息頭塊。 Message 類型的Headers屬性提供了這個功能。和WCF裡其它關鍵的類型一樣,使 用Headers屬性需要我在WCF API裡與其它類型交互,即MessageHeader、 MessageHeaders和EndpointAddress 類型。這些類型的名字已經暗示了他們的作 用。例如,MessageHeader是對SOAP消息頭塊的泛型CLR抽象;MessageHeaders, 廣義上說,是一組MessageHeader對象;EndpointAddress是對WS-Addressing endpoint規范的CLR抽象。具體使用的時候,這些類型提供了給Message插入、序 列化、編碼、反序列化、解碼消息頭塊,以及從反序列化的消息頭塊裡提取信息 的能力。本節,我們將會研究這些基本類型,以及如何和Message一起使用。

MessageHeader類型

WCF裡SOAP消息頭的基本構建單位就是MessageHeader類型,它的對象模型和 Message 類型很像。與Message一樣,MessageHeader是抽象類,它暴露了幾個返 回MessageHeader子類型實例的工廠方法。 MessageHeader類型也定義了幾個通 過XmlWriter或者XmlDictionaryWriter序列化MessageHeader內容的方法。

創建一個MessageHeader對象

MessageHeader類型上定義的幾個CreateHeader工廠方法。每個工廠方法接受 一組參數,但是表示消息頭塊的name (String)、namespace (String)和 value (Object)的三個參數一直存在。其它參數允許我們自定義序列化器,還有其它 SOAP消息頭塊的屬性如mustUnderstand、actor和relay。下面代碼演示了如何創 建一個簡單的帶有WS-Addressing 規范裡定義的MessageID 的MessageHeader對 象:

String WSAddNS =  "http://www.w3.org/2005/08/addressing";
MessageHeader header = MessageHeader.CreateHeader ("MessageID",
  WSAddNS, new UniqueId().ToString());
Console.WriteLine(header.ToString());

The following output is generated when this code executes:

運行結果如下:

<MessageID  xmlns="http://www.w3.org/2005/08/addressing">
  urn:uuid:
</MessageID>

注意到為了創建序列化為WS-Addressing MessageID (本例中使用String)消 息頭塊的MessageHeader 對象,必須知道XML namespace和MessageID的name。你 ,我是不知道,但是我是不願意記住WS-*規范裡的一對消息塊name.WCF架構師也 有相同的感受,他們已經為我們提供了創建符合WS-*規范的消息頭塊的方式。本 書裡我們會看到這幾種方式,本章裡的“MessageHeaders類型”一節就會講到這 些內容。

這裡要著重指出的是外貌可以構建表示自定義的消息頭塊的MessageHeader對 象,它不需要遵循WS-* 規范。例如,在消息發送到另一個消息參與者之前,訂 單處理系統或許要增加一個名為PurchaseOrderInfo的消息頭塊到Message上。因 此,從前面的例子看出,我們可以簡單地改變XML namespace、消息頭塊的name 和消息頭塊的值來滿足程序的需求。

MessageHeader header = MessageHeader.CreateHeader ("PurchaseOrderDate",
  "http://wintellect.com/POInfo", DateTime.Now);
Console.WriteLine(header.ToString());
This code generates the following output:

代碼輸出結果如下:

<PurchaseOrderDate  xmlns="http://wintellect.com/POInfo">
  2007-01-12T09:18:52.020824-04:00
</PurchaseOrderDate>

備注:正如你將在第九章裡看到的,WCF基礎結構可以使用消息契約為我們做 這些工作。當我們使用這種簡單、不易出錯的方法時,WCF基礎結構會執行類似 前面的代碼。同樣要重點指出的是MessageHeader本身沒什麼價值,想賦予其任 何意義,我們需要在Message對象裡引用這個對象。你會在本章後面的 “MessageHeaders類型“一節裡詳細學習這些內容。

序列化一個MessageHeader對象

MessageHeader類型一致了幾個序列化和編碼MessageHeader對象狀態的成員 。像Message類型一樣,大部分成員名字以Write開頭,並且接受一個XmlWrite或 者一個XmlDictionaryWriter作為參數。MessageHeader類型同樣定義了 protected的抽象方法OnWriteHeaderContents和protected 的虛方法 OnWriteStartHeader,它們允許MessageHeader的子類可以對MessageHeader的序 列化做更多控制。MessageHeader類型裡的Write方法會調用適當的protected方 法,因此把序列化的任務交給了繼承子類型。

備注:對於我來說,很難想象一個理由,在Message序列化的更大的上下文環 境外去序列化一個MessageHeader對象。換句話說,唯一一次你需要考慮 MessageHeader序列化,就是當你序列化一個Message的時候。因為Message類型 的上定義的Write方法只能序列化 SOAP envelope 和SOAP body。因此序列化 Message的時候,序列化MessageHeader對象是必須的。我們會在本章的後面一節 “MessageHeaders 類型“討論這個主題。

下面代碼演示了如何調用Write方法通過XmlDictionaryWriter去序列化一個 MessageHeader對象:

[Serializable]
sealed class PurchaseOrderInfo {
  internal Int32 PONumber;
  internal DateTime? IssueDate;
  internal Double? Amount;

  internal PurchaseOrderInfo(Int32 ponumber,
                              DateTime?  issueDate,
                              Double?  amount){
     PONumber = ponumber;
     IssueDate = issueDate;
     Amount = amount;
  }
}
class Program {
  static void Main(){
     // create an object to store in the  MessageHeader
     PurchaseOrderInfo poinfo = new PurchaseOrderInfo (1000,
                                                       DateTime.Now,
                                                       10.92);
     // create the MessageHeader
     MessageHeader header = MessageHeader.CreateHeader(
       "PurchaseOrderInfo", "http://wintellect.com/POInfo",  poinfo);

     MemoryStream stream = new MemoryStream();
     XmlDictionaryWriter writer =  XmlDictionaryWriter.CreateTextWriter(
       stream, Encoding.UTF8, false);
     // Serialize the MessageHeader via an  XmlDictionaryWriter
     header.WriteHeader(writer,  MessageVersion.Soap12WSAddressing10);
     writer.Flush();
     stream.Position = 0;
     // Show the contents of the Stream
     Console.WriteLine(new StreamReader(stream).ReadToEnd ());
  }
}

執行代碼,輸出如下結果:

<PurchaseOrderInfo  xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
     xmlns="http://wintellect.com/POInfo">
  <Amount xmlns="http://schemas.datacontract.org/2004/07/">
     10.92
  </Amount>
  <IssueDate  xmlns="http://schemas.datacontract.org/2004/07/">
     2007-01-11T15:06:25.515625-04:00
  </IssueDate>
  <PONumber xmlns="http://schemas.datacontract.org/2004/07/">
     1000
  </PONumber>
</PurchaseOrderInfo>

注意到結果裡包含PurchaseOrderInfo的子節點信息。駐留的這些信息是序列 化PurchaseOrderInfo 對象的副產品,而不是MessageHeader對象的直接作用結 果。為什麼,你也許會問,我們要在乎這些序列化的的消息頭裡的駐留信息嗎? 我們在乎因為許多消息頭塊都定義在WS-*規范裡,並且很多自定義消息頭的結構 和駐留消息條目一樣。概括地說,當序列化MessageHeader時,如果我們需要創 建駐留消息條目,我們必須傳遞一個對象給MessageHeader的序列化工廠方法, 或者MessageHeader的子類型,控制序列化工作。MessageHeader類型的子類型比 WCF默認的序列化器提供了更多控制。當然比開發我們自己的序列化器更加簡單 。因此,WCF API內部使用MessageHeader的子類型作為序列化WS-*消息頭塊的一 種方法。

WS-Addressing終結點規范

正如你在第二章裡看到的,WS-Addressing為SOAP消息提供了一種統一的、標 准化的尋址方式,這個標准的核心之一就是終結點規范。如其在WS-Addressing 定義的一樣,終結點都有與下面類似通用結構:

<wsa:EndpointReference xmlns:wsa="..."  xmlns:wnt="...">
  <wsa:Address>http://wintellect.com/OrderStuff</wsa:Address> ;
  <wsa:ReferenceParameters>
     <wnt:OrderID>9876543</wnt:OrderID>
     <wnt:ShoppingCart>123456</wnt:ShoppingCart>
  </wsa:ReferenceParameters>
</wsa:EndpointReference>

這些信息是SOAP消息裡的一個消息頭塊。記住MessageHeader類型是對SOAP消 息頭塊的CLR抽象,我們可以假定MessageHeader對象是對endpoint參考規范的 CLR抽象。從前面的結構裡注意到,這些item隸屬於EndpointReference節點,並 且和上一節提到的一樣,它們帶來了一些有趣的序列化挑戰。

如果我們要構建一個可以序列化符合全部規范的MessageHeader對象 (address和參數信息條目),我們有三種選擇:

·定義一個表示終結點的引用,並且傳遞給它一個CreateHeader工廠方法的 實例作為Object參數。

·MessageHeader的子類型使用自定義序列化方式。

·定義一個表示終結點規范的類型和MessageHeader的子類型。

在嘗試了第一種方法以後,我們發現這個方式不可行(你會在第九章詳細學 習序列化內容),因此強制我們考慮後面的2種方法。我們可以使用第二種方法, 但是如果我們重構我們的設計,我們會很快發現我們的程序需要一個表示終結點 的類型。話句話說,我們提出了一個需要自己定義終結點參考的情形。由於這些 事實,WCF團隊使用了第三種方法。他們定義了EndpointAddress來表示一個WS- Addressing終結點參考規范並且MessageHeader的子類型。通過這種組合我們可 以通過MessageHeader對象表示一個終結點參考規范並且完全序列化它。你會在 下一節MessageHeaders學習到詳細內容。

MessageHeader外傳

MessageHeader類型的其它幾個方面也值得提一下。最顯著的一點就是在實例 化以後沒有直接獲取MessageHeader值的方法。咋一看,這還真是個問題,特別 是當我們查詢反序列化後的SOAP消息頭塊的內容時。Message 類型的Headers屬 性提供了解決這個困難的辦法,並且這個類型定義了提取MessageHeader內容的 機制。我們會在下一節的“MessageHeaders 類型“裡詳細介紹。

另外一個MessageHeader類型的奇怪成員就是就是IsReferenceParameter只讀 屬性。在我們查詢SOAP 消息頭塊的內容時會非常有用,這個屬性表示 MessageHeader對象是否是一個WS-Addressing引用參數或者引用屬性。你或許會 對自己說,“你剛才不是說一個引用參數/屬性在作用上,MessageHeader 對象 表示一個終結點引用?“。是的,我這樣說過,但是這還不能解決問題,就是判 斷一個MessageHeader對象是一個引用參數或者是引用屬性.

考慮SOAP 消息裡的To項目的結構,如下所示:

<S:Envelope xmlns:S="..." xmlns:wsa="..."  xmlns:wnt="... ">
  <S:Header>
     ...
     <wsa:To>http://wintellect.com/OrderStuff</wsa:To>
     <wnt:OrderID  wsa:IsReferenceParameter="true">9876543</wnt:OrderID>
     <wnt:ShoppingCart wsa:IsReferenceParameter="true">
       123456
     </wnt:ShoppingCart>
      ...
  </S:Header>
<S:Body>

和演示的一樣,OrderID 和ShoppingCart是獨立的消息頭塊,並且表示符合 終結點規范的參數的引用。與To URI一起使用,他們可以用來創建一個終結點引 用,因此他們和別的消息頭塊不同。我們可以容易地構建表示To終結點邏輯引用 的OrderID和ShoppingCart條目的MessageHeader對象,但是區分這些 MessageHeader對象可沒那麼容易,除非有IsReferenceParameter屬性。換句話 說,當反序列化SOAP消息為Message對象,並且查詢MessageHeader對象的時候, 我們能夠通過檢查IsReferenceParameter屬性確定哪些對象是引用參數。一旦確 定了那些是引用參數,我們可以將其與To URI一起使用,因此可以高效地創建一 個終結點引用。你會在下一節裡學習關於這個問題的更多內容。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved