WCF專題系列(3):深入WCF尋址Part 3—消息過濾引擎
[2] WCF專題系列(3):深入WCF尋址Part 3—消息過濾引擎
概述
通過前面兩篇的介紹,對Web服務尋址規范以及在WCF開發中終結點地址有了深入的認識。本文我們繼續深入WCF尋址第三部分內容,當消息傳入時,如何來確定匹配的終結點,就是我們本文要講到的消息篩選引擎。在WCF中,消息篩選器引擎包括兩個重要的組成部分:篩選器和篩選器表。
認識消息篩選器
在WCF中當有消息傳入時,它使用消息篩選器來確定匹配的終結點,每個終結點實際上關聯著兩個篩選器:一個地址篩選器和一個契約篩選器。地址篩選器確定傳入消息是否匹配終結點的“To”地址和任何必需的地址報頭,而契約篩選器則確定它是否匹配終結點的契約,兩個篩選器都被調度程序用來確定目標終結點。
在WCF中,所有的消息篩選器都繼承于MessageFilter抽象基類,系統內置了幾種的消息篩選器,如圖1所示:
圖1
EndpointAddressMessageFilter:作為默認的地址篩選器,它會將SOAP消息中的“To”地址與終結點地址進行比較,預期它們完全匹配,也會傳入消息中獲得的尋址報頭和終結點要求的一組尋址報頭進行比較,要使最終匹配的結果返回true,必須滿足以下兩個條件:
1. 篩選器的地址統一資源標識符 (URI) 必須與消息 To 標頭中的統一資源標識符相同。
2. 篩選器地址中的每個終結點參數都必須在消息中找到一個與之匹配的標頭。
ActionMessageFilter:作為默認的契約篩選器,它根據傳入的SAOP消息中“Action”值和契約上的操作進行比較,確定是否匹配匹配。該篩選器在初始化時將包含一個操作字符串列表,如果篩選器的列表中的任一操作與消息或消息緩沖區中的 Action 標頭匹配,則 Match 方法返回 true。 如果該列表為空,則將該篩選器視為全匹配型篩選器,任何消息或消息緩沖區都與該篩選器匹配,并且 Match 返回 true。 如果篩選器列表中沒有任何操作與消息或消息緩沖區中的 Action 標頭匹配,則 Match 返回 false。 如果消息中不存在任何操作且篩選器的列表非空,則 Match 返回 false。
PrefixEndpointAddressMessageFilter:此篩選器與 EndpointAddressMessageFilter 執行相同的查詢,不同的是測試消息是否與終結點地址相匹配是由“最長前綴匹配”完成的。這表示篩選器中指定的 URI 不需要與消息的 URI 完全匹配,不過必須作為前綴包含在該 URI 中。例如,如果篩選器指定地址“http://www.foo.com”,并且消息是發送給“http://www.foo.com/customerA”,則將滿足篩選器查詢條件的 URI 部分,但是篩選器查詢的報頭部分仍需要完成。
MatchAllMessageFilter:該篩選器將導致所有消息都匹配給定終結點,看一下它的Match方法實現,除非消息為空,否則就返回True:
[DataContract] public class MatchAllMessageFilter : MessageFilter { public override bool Match(Message message) { if (message == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); } return true; } }
MatchNoneMessageFilter:該篩選器將導致所有消息都不匹配,看一下它的Match方法實現,除非消息為空,否則就返回False:
[DataContract] public class MatchNoneMessageFilter : MessageFilter { public override bool Match(Message message) { if (message == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); } return false; } }
XpathMessageFilter:使用 XPath 1.0 表達式來指定匹配的條件。
除此之外,我們可以自定義自己的消息篩選器,在本系列的后面將會講到。
篩選器工作原理
正如在前面所看到的,MessageFilter提供了所有篩選器的基類,篩選器中的Match方法用于確定消息是否滿足篩選器的條件。如下面的代碼片段中,我們定義兩個ActionMessageFilter和EndpointAddressMessageFilter,然后創建一個Message,看看它們最終匹配的結果:
static void Main(string[] args) { // 創建兩個ActionMessageFilter實例 ActionMessageFilter actionFilter1 = new ActionMessageFilter("Add", "Sub"); ActionMessageFilter actionFilter2 = new ActionMessageFilter("Mul"); // 創建兩個EndpointAddressMessageFilter實例 EndpointAddressMessageFilter addressFilter1 = new EndpointAddressMessageFilter ( new EndpointAddress("http://localhost:8887/Calculator") ); EndpointAddressMessageFilter addressFilter2 = new EndpointAddressMessageFilter ( new EndpointAddress("http://www.cnblogs.com/terrylee") ); // 創建一個Message,設置Action和To Message message = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "myBody"); message.Headers.Action = "Add"; message.Headers.To = new Uri("http://localhost:8887/Calculator"); // 測試匹配結果 bool actionResult1 = actionFilter1.Match(message); bool actionResult2 = actionFilter2.Match(message); bool addressResult1 = addressFilter1.Match(message); bool addressResult2 = addressFilter2.Match(message); // 輸出結果 Console.WriteLine("The result of filter:"); Console.WriteLine(actionResult1); Console.WriteLine(actionResult2); Console.WriteLine(addressResult1); Console.WriteLine(addressResult2); Console.ReadLine(); }
輸出結果如圖2所示:
圖2
在該示例中,由于我們創建的Message對象,最終的SOAP消息包如下代碼所示:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"> <s:Header> <a:To s:mustUnderstand="1">http://localhost:8887/Calculator</a:To> <a:Action s:mustUnderstand="1">Add</a:Action> </s:Header> <s:Body> </s:Body> </s:Envelope>
所以這里匹配上的是actionFilter1和addressFilter1。注意,一旦構造篩選器,篩選器使用的條件無法更改,因為篩選器表無法檢測更改。修改篩選器的條件的唯一方法是構造一個新的篩選器,然后刪除現有篩選器。