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

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

編輯:關於.NET

MessageHeaders類型

因為SOAP消息可能包含很多消息頭塊,所以在一個Message類型裡,我們需要 一種表示一組消息頭塊對象的方法。MessageHeaders就是這個作用,並且它定義 了一個MessageHeaders 類型的只讀屬性Headers。Headers屬性是我們在Message 裡增加、修改、查詢和移除MessageHeader的主要方式。在某種意義上,本節主 要是講解MessageHeaders類型,以及可以應用到Message類型的Headers屬性上的 所有信息。與Message相反,在實例化一個Message之後,我們可以隨便修改 Headers屬性的內容。MessageHeaders是一個具體類,而不是抽象類,它不包含 工廠方法。這一點值得注意,因為本章討論過的類型都是抽象的並且定義了工廠 方法。

像前面提到的一樣, MessageHeaders,在一定層次上,是一組 MessageHeader對象。MessageHeader類型的對象模型,奇怪的是少了一個可以返 回MessageHeader對象集合的成員。作為替代,MessageHeaders實現了 IEnumerable<MessageHeaderInfo> and IEnumerable接口。這意味著我們 可以簡單地迭代MessageHeaders類型來查看所有的消息頭塊(在MessageHeaders 對象賦值以後)。

注意

為了完整,我必須提下MessageHeaderInfo類型,它是MessageHeader的基類 。MessageHeaderInfo定義了幾個表示SOAP消息頭塊的屬性,比如:Actor、 MustUnderstand等等。太白地說,我們看不出這個類型存在的理由,因為 MessageHeader是抽象的。

創建一個MessageHeaders對象

MessageHeaders類型定義了三個公開的構造函數。這裡要著重指出的是絕大 多數開發人員都不會直接使用這些構造函數,因為Message類型(子類型)底層 機制會為你調用其中的一個構造函數。如果你要選擇繼承Message類型的話,或 許需要調用其中一個構造函數去設置Message的消息頭部分。

其中一個構造函數接受MessageHeaders類型的參數。構造函數會對 MessageHeaders執行深拷貝,並把它存儲在MessageHeaders實例裡。

另外一個構造函數接受一個MessageVersion類型的參數,如你所料,這是設 置MessageHeaders 實例的SOAP version和WS-Addressing version。最後一個構 造函數接受一個MessageVersion類型和一個Int32類型的參數。這個構造函數設 置SOAP和WS-Addressing的版本,同樣包括內部消息頭塊list裡元素的個數。記 住實際元素的個數可以超過Int32設置的個數。如果我們知道將要增加到 MessageHeaders對象裡的消息頭的個數,使用這個重載方法,它會提升性能,因 為在對象的整個生命周期裡早期的時候,已經設置好了合適的存儲空間。

添加一個MessageHeader

一旦MessageHeaders對象實例化完畢,我們需要給它增加一個或者多個 MessageHeader對象。MessageHeaders類型定義了接受一個MessageHeader 對象 作為參數的Add方法。然後把插入MessageHeader 對象插入到消息頭塊列表的最 末端。

如果我們需要把MessageHeader對象插入到特定的位置,我們可以使用Insert 方法。它接受一個Int32 和MessageHeader類型的參數。Int32 類型的參數表示 要插入的位置,MessageHeader參數是要插入的對象。非常有意思的是 MessageHeaders把MessageHeader 對象存放在一個數組結構中。如果我們傳遞的 索引大於數組的大小,方法會拋出一個ArgumentOutOfRangeException。

獲取MessageHeader的值

當一個程序接收、解碼和反序列化一個stream到Message對象的時候,我們經 常需要獲取一個或者多個消息頭塊的值。因為MessageHeader類型提供了多種方 式,我們必須求助於MessageHeaders類型。

一種方式,我們在MessageHeaders對象裡獲取特定的MessageHeader,就是使 用索引。為了找到特定消息頭塊的索引,我們可以調用兩個FindHeader方法。它 們都接受表示nam和namespace的String參數。其中一個方法接受一個表示能夠與 消息頭塊交互的actor的String參數。它們的返回值都是Int32。如果沒有匹配的 消息頭塊,FindHeader回返回-1。如果找到多個消息頭塊,會返回第一個匹配的 消息頭塊的索引。

備注

我的觀點,這不是一個良好的設計, 它違反了Microsoft文檔裡已經規定的 最佳實踐和關於framework設計的內部標准。它應該命名為為TryFindHeader或者 如果沒有找到匹配的消息頭就應該拋出一個異常。拋開我的看法,當調用 FindHeader方法的時候,我們必須檢查返回的值是否為-1。

在找到消息頭塊的索引以後(只要不是-1),我們隨後就可以檢查消息頭塊 的值。為此,我們調用其中一個GetHeader<T> 方法。重載的方法接受各 種參數,包括一個消息頭塊的索引和一個自定義的序列化器。其中三個重載方法 接受的String參數可以映射到FindHeader方法接受的參數上。內部來看,這些重 載方法調用適當的FindHeader方法,並且檢查返回的值是否是-1.與FindHeader 相反,如果沒有找到匹配的消息頭塊,GetHeader<T>方法會拋出個異常。

復制一個MessageHeaders對象

MessageHeaders類型提供了幾種機制從MessageHeaders 對象去復制一個或者 所有的消息頭塊。為了看看這個功能有何用處,思考一下,當需要根據一個接受 到的Message來創建一個新的Message的時候,需要什麼數據。如果接受的 Message包含一個PurchaseOrderInfo消息頭塊,我們或許需要包含一個復制的消 息頭塊在回復的Message 裡。雖然可以使用相同的值重新創建一個新的消息頭塊 ,但是復制一個存在的消息頭塊到一個新的Message裡會簡單很多。

這兩個CopyHeaderFrom方法提供了復制一個消息頭塊的值到MessageHeaders 實例裡的能力。它們都接受Int32參數作為消息頭塊的索引。並且會把消息頭塊 也放到內部數組的末端,沒有提供指定位置的是方法。其中一個 CopyHeaderFrom方法接受一個Message對象作為參數,而另外一個接受一個 MessageHeaders對象作為參數。內部來看,前者通過Message類型裡的Headers屬 性調用後者。

兩個CopyHeadersFrom方法提供了復制整個MessageHeaders 對象內容的能力 、同樣有一個方法接受一個Message作為參數,另外一個方法接受一個 MessageHeaders參數。源數據消息頭塊被追加到目的消息頭塊的尾部。換句話說 ,這個操作不是一個替換,而是對現有消息頭塊的串聯。這可能有一些意外的結 果,如下所示:

// create a Message創建一個消息
Message message = Message.CreateMessage(
     MessageVersion.Soap12WSAddressing10,
     "urn:SomeAction",
     "Hello WCF");
// add two new headers to the Message
message.Headers.To = new Uri ("http://wintellect.com/Original");
message.Headers.Add(MessageHeader.CreateHeader("test", "test",  "test"));

// create a new Message創建一個新的消息
Message message2 = Message.CreateMessage(
     MessageVersion.Soap12WSAddressing10,
     "urn:SomeAction2",
     "Hello WCF2");
// add two new headers to the Message
message2.Headers.To = new Uri("http://wintellect.com/Test");
message2.Headers.Add(MessageHeader.CreateHeader("test", "test",  "test"));

// copy the headers from the first Message into the second  one復制消息頭塊
message2.Headers.CopyHeadersFrom(message);

// show the contents
Console.WriteLine(message2.ToString());

When this code executes, the following output is generated:

執行代碼,產生如下結果:

<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">urn:SomeAction2</a:Action>
     <a:To  s:mustUnderstand="1">http://wintellect.com/Test</a:To>
     <test xmlns="test">test</test>
     <a:Action  s:mustUnderstand="1">urn:SomeAction</a:Action>
     <a:To  s:mustUnderstand="1">http://wintellect.com/Original</a:To>
     <test xmlns="test">test</test>
  </s:Header>
  <s:Body>
     <string  xmlns="http://schemas.microsoft.com/2003/10/Serialization/">
       Hello WCF2
     </string>
  </s:Body>
</s:Envelope>

哎呦!這個Message顯然不對。事實上,CopyHeaderFrom方法遇到相同的問題 (重復的消息頭塊)。換句話說,復制消息頭是件頭疼的事情,開發人員需要檢 查目標Message裡是否有重復的消息頭塊、

序列化一個MessageHeaders對象

MessageHeaders類型定義了幾個序列化MessageHeaders對象部分或者整體的 方法。與Message和MessageHeader類型一樣,MessageHeaders 類型上的序列化 方法也是以單詞Write開頭。最簡單的方法是WriteHeader方法。如其名字暗示的 一樣,這個方法就是序列化一個消息頭塊。它接受一個Int32 和一個 XmlDictionaryWriter作為參數。Int32表示消息頭塊的索引, XmlDictionaryWriter,正如你猜測的一樣,執行實際的序列化和編碼工作。 WriteHeader方法調用其它的MessageHeaders的序列化方法:WriteStartHeader 和WriteHeaderContents。WriteStartHeader方法,也和其名字暗示的一樣,序 列化開發的消息頭塊,而WriteHeaderContents方法序列化消息頭塊的內容。

沒有一步序列化全部MessageHeaders內容的機制。唯一的方法就是迭代所有 的消息頭塊,並序列化每一個消息頭塊。實際上,我們很少會有這樣的需求,就 是在序列化一個Message的上下文環境外,去序列化消息頭塊。因此,Message類 型定義了序列化真個內容的WriteMessage方法。此方法在Message類型上的實現 ,會迭代並序列化每一個消息頭塊。

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