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。注意,一旦构造筛选器,筛选器使用的条件无法更改,因为筛选器表无法检测更改。修改筛选器的条件的唯一方法是构造一个新的筛选器,然后删除现有筛选器。