摘要

WCF服务的分发器可以在把消息传递至服务实例的方法之前或在接收到服务的响应消息之后,翻译并检查这些消息。如果你需要实现消息检查功能,那么你首先需要自定义检查消息的服务行为,然后通过硬编码将服务行为嵌入到代码中或扩展WCF服务行为并通过配置文件配置消息检查功能。

检查消息

WCF服务模型的一个有趣的特性是分发消息至服务的方法之前翻译消息,在离开服务方法后进入向客户端回传的传从通道堆栈之前再次翻译消息。采用消息翻译,可以在接收和发送消息的时候检查消息。你还可以在服务实例对象的方法处理消息之前或向客户端发送响应消息之前修改消息。尽管你在使用它时必须小心地处理以避免为你的系统带来安全方面的隐患或漏洞,消息检查仍然是一项非常有用的技术。
你可以创建一个消息检查器来翻译消息;即创建一个实现IDispatchMessageInspector接口的类,然后定义一个行为把该对象插入到WCF基础架构中;该行为决定了消息翻译的范围。如果你指定消息翻译为一个服务行为,那么所有发送至服务的消息都将被翻译。你还可以指定消息翻译为一个操作行为,端点行为或者合约行为;相应地,经过操作、端点和合约的消息都将被翻译。
你可以在客户端程序或者服务中实现消息检查。在下面的练习中,你将看到如何创建并集成一个消息检查器至服务端的WCF运行时的分发机制中。如果服务接收到消息,消息检查器将显示接收到的消息。如果你想在客户端检查消息,那么需要实现IClientMessageInspector接口。
为ShippingCartService服务创建消息检查器
1. 在Visual Studio的解决方案窗口中,选择ShoppingCartService项目。在项目菜单中,选择添加一个新的类文件,并命名为ShoppingCartInspector.cs
2. 在ShoppingCartInspector.cs文件中,添加下面的语句
1 using System.ServiceModel.Dispatcher;
2 using System.ServiceModel.Description;

3. 修改ShoppingCartInspector类的定义,使其实现IDispatchMessageInspector接口

1 public class ShoppingCartInspector : IDispatchMessageInspector
2 {
3 }

IDispatchMessageInspector接口定义两个方法,使用这两个方法你可以查看和修改进入和离开服务的消息。
4. 在IDispatchMessageInspector上面点击右键,指向实现接口à实现接口。Visual Studio自动生成IDispatchMessageInspector接口的两个方法。一个名为AfterReceiveRequest,在服务的方法被访问之前调用该方法;另外一个为BeforeSendReply,在服务的方法结束之前调用该方法。请注意两个方法的第一个参数都是Message对象类型。该参数的值为刚刚收到或者即将发出的消息对象。你在方法里可以修改该消息对象的内容,你对该对象所作的改动都将传递至服务或者返回到客户端,这取决于该消息是客户端发送的请求消息还是服务的响应消息。由于这个原因,你在修改消息时应格外小心,不要做了相反的修改。
5. 修改Visual Studio生成的AfterReceiveRequest方法的内容

1 public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request,
2 System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
3 {
4 Console.WriteLine("Message received: {0}\n{1}\n\n", request.Headers.Action, request.ToString());
5 return null;
6 }

第一行代码显示消息的行为和消息的内容。在某些情况下,关联AfterReceiveRequest方法的消息和BeforeSendReply方法的消息是非常有用的。如果你查看BeforeSendReply方法,你可以看到该方法的第二个参数为correlationState。如果你需要关联请求和响应消息,你可以在AfterReveiveRequest方法中创建一个唯一的标识符并返回改值。WCF运行时将把相同的标识符作为参数传递至方法BeforeSendReplyMethod。在当前的练习中,你没有关联任何请求和回复消息,所以AfterReceiveRequest方法将返回null。

6. 修改Visual Studio生成的BeforeSendReply方法的内容
1 public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
2 {
3 Console.WriteLine("Message sent: {0}\n{1}\n\n", reply.Headers.Action, reply.ToString());
4 }

7. 重新生成ShoppingCartService方案

创建自定义行为

使用服务行为,你可以把ShoppingCartInspector实现的行为集成到WCF运行时。然而WCF并未提供内建的"IntegrateShoppingCartInspector"服务行为。幸运的是,创建一个实现IServieBehavior接口的类以实现集成服务行为,这并不难。
IServiceBehavor接口定义了三个方法,其实现类必须实现这三个方法,才可能在WCF基础架构中扮演服务行为。这三个方法如下:
  • AddingBindingParameters,一些行为可以接收额外的数据项作为参数,并传递至绑定元素,管理员或者开发人员可以在BindingParameterCollection中提供这些信息并将这些信息传递至该方法。WCF运行时针对服务侦听的每个URI都调用一次AddingBindingParameters方法。
  • ApplyDispatcherBehavior, 使用该方法,你可以修改ServiceHost对象所寄宿服务的行为。ServiceHost对象被作为该方法的第二个参数传入。使用该方法可以执行的任务包括添加自定义错误处理,或者消息检查对象。
  • Validate, WCF运行时调用该方法以验证服务是否满足你自己定义的需求。比如,你可以检查该方法的第一个参数(传入的服务描述对象),如果该服务的合约与期望的不一致,你可以拒绝并抛出一个异常。
如果你需要实现一个操作行为、服务端点行为、或者合约行为,那么你需要实现IOperationBehavior、IEndpointBehavior和IContractBehavior接口。这些接口与IServiceBehavior非常相似。他们都对外暴露AddingBindingParameters、ApplyDispatcherBehavior和Validate方法,尽管这些方法接收的参数不同(因为这些接口的范围分别指向操作,服务端点和合约),但是它们的目的都一样。此外,这三个接口提供了一个名为ApplyClientBehavior的方法。这个方法接收一个名为ClientRuntime的参数,该参数为客户端WCF运行时对象。你可以修改该对象的属性以配置客户端运行时的运行方式;在需要检查或者管理客户端发送和接口的消息时,你还可以插入一个消息检查器至客户端运行时。
在下面的练习中,你将实现一个IServiceBehavir接口,并创建一个可以用以添加消息检查器服务行为至ShoppingCartService服务
为ShoppingCartService服务创建一个服务行为
1. 在文件ShoppingCartInspector.cs中,添加一个名为ShoppingCartBehavior的public类,该类实现IServiceBehavior接口
public class ShoppingCartBehavior : IServiceBehavior

2. 在IServiceBehavior上点击右键,指向实现接口à实现接口。 Visual Studio将自动生成AddBindingParameters、ApplyDispatcherBehavior和Validate方法

3. 注释掉AddingBindingParameters和Validate方法内的throw语句。

4. 修改ApplyDispatcherBehavior方法
 1 public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
2 System.ServiceModel.ServiceHostBase serviceHostBase)
3 {
4 foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
5 {
6 foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
7 {
8 endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new ShoppingCartInspector());
9 }
10 }
11 }

上都代码首先迭代ChannelDispatchers;然后再迭代每个ChannelDispatcher的EndpointDispatcher;然后添加ShoppingCartInspector对象到每个EndpointDispatcher上。随后,当一个EndpointDispatcher对象分发一个服务方法或者一个服务方法返回到EndpointDispatcher对象,消息都将通过ShoppingCartInspector对象传输。

5. 打开Programm.cs文件,修改main方法中,以使ShoppingCartService运行时,添加ShoppingCartServiceBehavior实现的服务行为
 1 static void Main(string[] args)
2 {
3 ...
4 ServiceHost host = new ServiceHost(typeof(ShoppingCartService.ShoppingCartService));
5 host.AddServiceEndpoint(typeof(ShoppingCartService.IShoppingCartService), customBinding, "net.tcp://localhost:8090/ShoppingCartService");
6
7 host.Description.Behaviors.Add(new ShoppingCartService.ShoppingCartBehavior());
8
9 host.Open();
10 ...
11 }

6. 在非调适模式下,运行解决方案。当服务启动后,在客户端控制台窗口中按ENTER键。

客户端应用程序按照之前的方式运行(但是这次有点慢)。服务端的控制台显示接收和发送的SOAP消息,其结果如下图所示:
7. 在客户端控制台窗口中按ENTER键关闭客户端程序;在宿主程序控制台窗口中,按ENTER键停止服务。

定义行为扩展元素

在上面的练习中,通过显示地添加该行为到ServiceHost对象的Description属性的Behavior集合,你硬编码了ShoppingCartBehavior行为至服务宿主程序。但是,该行为是一个不重要功能行为的例子,一般地这类行为应通过管理员在配置文件中开启或者关闭。
为了支持在配置文件中配置行为,你必须提供一个行为扩展元素。行为扩展元素是一个类,当WCF运行时启动一个服务并读取配置文件时,使用扩展元素类配置一个行为。行为扩展元素允许WCF运行时发现实现行为的类型,然后实例化该类型,如果需要则配置该对象对应的属性。
实现行为扩展元素的最简单方式是扩展System.ServiceModel.Configuration命名空间下的BehaviorExtensionElement类。BehaviorExtensionElement类是一个抽象类,它提供了所需要的大多数功能,在必要的时候你可以重写这些方法。在扩展类中,你必须提供一个名为BehaviorType的属性(它返回该行为的类型)和一个名为CreateBehavior方法(它实例化该行为)。
在下面的练习中,你将创建一个名为ShoppingCartBehaviorExtensionElement的 行为扩展元素,然后更新ShoppingCartService的配置文件以引用该行为扩展元素。
为ShoppingCartBehavior行为创建一个行为扩展元素
1. 在Visual Studio中,添加System.Configuration引用到ShoppingCartService项目
2. 返回到ShoppingCartInspector.cs文件,然后添加下面的using语句
using System.ServiceModel.Configuration;

3. 添加一个名为ShoppingCartBehaviorExtensionElement的public类,该类继承BehaviorExtensionElement类

4. 在ShoppingCartBehaviorExtensionElement中,重写BehaviorType属性
1 public override Type BehaviorType
2 {
3 get { return typeof(ShoppingCartBehavior); }
4 }

5. 在ShoppingCartBehaviorExtensionElement中,重写CreateBehavior方法

protected override object CreateBehavior()
{
return new ShoppingCartBehavior();
}
6. 重新生成解决方案
下一步就是从ShoppingCartHost程序中移除硬编码的ShoppingCartBehavior行为;然后通过配置文件添加该扩展行为。你将在配置文件中创建一个匿名服务行为,该行为将自动为服务宿主程序获取并引用。
配置ShoppingCartService服务使用ShoppingCartBehavior行为
1. 打开ShoppingCartHost项目下的program.cs文件
2.修改main方法,注释掉硬编码的ShoppingCartBehavior行为
 1 static void Main(string[] args)
2 {
3 ...
4 ServiceHost host = new ServiceHost(typeof(ShoppingCartService.ShoppingCartService));
5 host.AddServiceEndpoint(typeof(ShoppingCartService.IShoppingCartService), customBinding, "net.tcp://localhost:8090/ShoppingCartService");
6
7 //host.Description.Behaviors.Add(new ShoppingCartService.ShoppingCartBehavior());
8
9 host.Open();
10 ...
11 }

3. 使用服务配置管理工具打开app.config
4. 在配置面板,展开高级文件夹,然后展开扩展节点,然后点击行为元素扩展
5. 在行为元素扩展面板的底部,点击"创建"按钮。在扩展配置元素编辑对话框中的Name处输入messageInspector。

6. 选择类型后的按钮,然后定位到*\WCF\Step.by.Step\Solutions\Chapter11\CustomBinding\ShoppingCartHost\bin\Debug下的ShoppingCartService.dll文件,然后在点击打开,你可以发现ShoppingCartBehaviorExtensionElement类型将自动填充。点击打开。
 
7. 然后点击扩展配置元素编辑对话框中点击"确认"按钮,扩展元素messageInspector将添加到行为元素扩展列表中。
8. 在高级à服务行为节点,点击创建新的服务行为配置。
9. 在右边面板中,清空行为的Name属性的值。然后点击右下的"添加"按钮,选择"messageInspector",然后点击确认按钮。完成后如下图所示:
10. 保存配置文件,然后退出服务配置管理工具
11. 在Visual Studio中,打开app.config,其内容应如下所示:
 1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3 <connectionStrings>
4 <add name="AdventureWorksEntities" connectionString="metadata=res://*/ProductsModel.csdl|res://*/ProductsModel.ssdl|res://*/ProductsModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=localhost;initial catalog=AdventureWorks;integrated security=False; user id=ap_wcf; password=ap_wcf; multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
5 </connectionStrings>
6
7 <system.serviceModel>
8 <behaviors>
9 <serviceBehaviors>
10 <behavior>
11 <messageInspector />
12 </behavior>
13 </serviceBehaviors>
14 </behaviors>
15 <extensions>
16 <behaviorExtensions>
17 <add name="messageInspector" type="ShoppingCartService.ShoppingCartBehaviorExtensionElement, ShoppingCartService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
18 </behaviorExtensions>
19 </extensions>
20 </system.serviceModel>
21 </configuration>

12. 在非调适模式下运行解决方案。在ShoppingCartClient控制台中,按ENTER键。客户端程序和服务将和前一个练习的结果一致。在寄宿服务的控制台窗口中显示了ShoppingCartInspector对象输出的消息。

13. 在客户端控制台窗口中按ENTER键关闭客户端程序;在宿主程序控制台窗口中,按ENTER键停止服务。
通过运行绝国,你可以非常容易地验证匿名行为ShoppingCartInspector被ShoppingCartService服务从配置文件中自动识别。在本例中我们使用了匿名服务行为,那么匿名服务行为和命名服务行为有什么区别呢?当且仅当在配置文件中定义了服务时,才使用命名服务行为;如果使用代码定义服务,那么我们就需要使用匿名服务行为(比如本例)。如果你使用代码定义服务,但是设置了命名的服务行为,那么通过ShoppingCartInspector对象的消息将不会在ShoppingCartHost控制台窗口中显示。
参考
MSDN, Extending Dispatchers http://msdn.microsoft.com/en-us/library/ms734665.aspx

作者: On the road.... 发表于 2011-07-23 14:49 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"