在 WCF 的通訊模型中,Message 類別是 SOAP 訊息的 CLR 抽象表示。正如第二章所述,SOAP 訊息標頭區塊被基礎結構用來承載定址、路由與安全性資訊。WCF 作為完整支援 SOAP 的訊息處理平台,提供了一套完整的工具來建立、序列化與解析 SOAP 標頭。Message 類別的 Headers 屬性即是核心入口,配合 MessageHeader、MessageHeaders 與 EndpointAddress 等關鍵型別,開發者能完整掌控標頭的插入、編碼、解碼與資訊萃取。
MessageHeader 是抽象類別,其物件模型與 Message 相似,透過多個 CreateHeader 工廠方法建立實例。每個方法至少需指定三個參數:名稱、命名空間與值。其他參數可自訂序列化器、mustUnderstand、actor 與 relay 等 SOAP 標頭屬性。
以下範例示範如何建立符合 WS-Addressing 規範的 MessageID 標頭:
String WSAddNS = "https://www.w3.org/2005/08/addressing";
MessageHeader header = MessageHeader.CreateHeader("MessageID", WSAddNS, new UniqueId().ToString());
Console.WriteLine(header.ToString());
輸出結果:
<MessageID xmlns="https://www.w3.org/2005/08/addressing">
urn:uuid:
</MessageID>
WCF 架構師深知開發者不願背誦 WS-* 規範中的名稱與命名空間,因此內建了更便利的建立方式,稍後在 MessageHeaders 章節將詳細介紹。
同樣的機制也適用於自訂標頭,例如訂單處理系統需在訊息中加入採購單日期:
MessageHeader header = MessageHeader.CreateHeader("PurchaseOrderDate",
"https://wintellect.com/POInfo", DateTime.Now);
Console.WriteLine(header.ToString());
輸出:
<PurchaseOrderDate xmlns="https://wintellect.com/POInfo">
2007-01-12T09:18:52.020824-04:00
</PurchaseOrderDate>
註記: 實務上常透過訊息契約由 WCF 基礎結構自動處理,第九章將深入探討。MessageHeader 本身無實質意義,必須加入 Message 物件才能發揮作用。
MessageHeader 定義多個以 Write 開頭的方法,接受 XmlWriter 或 XmlDictionaryWriter 進行序列化與編碼。核心邏輯委派給受保護的抽象方法 OnWriteHeaderContents 與虛擬方法 OnWriteStartHeader,讓子類別能精細控制序列化行為。
[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() {
PurchaseOrderInfo poinfo = new PurchaseOrderInfo(1000, DateTime.Now, 10.92);
MessageHeader header = MessageHeader.CreateHeader("PurchaseOrderInfo", "https://wintellect.com/POInfo", poinfo);
MemoryStream stream = new MemoryStream();
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8, false);
header.WriteHeader(writer, MessageVersion.Soap12WSAddressing10);
writer.Flush();
stream.Position = 0;
Console.WriteLine(new StreamReader(stream).ReadToEnd());
}
}
輸出結果顯示,PurchaseOrderInfo 的子節點(Amount、IssueDate、PONumber)自動被序列化為巢狀 XML 元素,這是預設資料契約序列化器的行為。若需更精細控制,可繼承 MessageHeader 覆寫序列化邏輯,比開發自訂序列化器更簡便。
WS-Addressing 定義了統一的 SOAP 訊息定址方式,其核心為端點參考結構:
<wsa:EndpointReference xmlns:wsa="..." xmlns:wnt="...">
<wsa:Address>https://wintellect.com/OrderStuff</wsa:Address>
<wsa:ReferenceParameters>
<wnt:OrderID>9876543</wnt:OrderID>
<wnt:ShoppingCart>123456</wnt:ShoppingCart>
</wsa:ReferenceParameters>
</wsa:EndpointReference>
這些資訊以單一訊息標頭形式存在。為了完整序列化符合規範的端點參考(含位址與參數集合),WCF 採用第三種策略:定義 EndpointAddress 類別同時繼承 MessageHeader,結合兩者優勢完整支援端點參考的建構與序列化,詳見下一節。
實例化後無法直接取得 MessageHeader 的值,這看似不便,但 Message.Headers 屬性提供了解析與萃取反序列化標頭內容的完整機制,下節將詳述。
此唯讀屬性標示該標頭是否為 WS-Addressing 參考參數。觀察以下 SOAP 結構:
<S:Header>
<wsa:To>https://wintellect.com/OrderStuff</wsa:To>
<wnt:OrderID wsa:IsReferenceParameter="true">9876543</wnt:OrderID>
<wnt:ShoppingCart wsa:IsReferenceParameter="true">123456</wnt:ShoppingCart>
</S:Header>
OrderID 與 ShoppingCart 為獨立標頭,配合 To URI 共同構成端點參考。反序列化時,透過檢查 IsReferenceParameter 即可識別參考參數,進而高效建構端點參考物件。