基于IIS的WCF服务寄宿(Hosting)

  基于IIS的服务寄宿依赖于两个重要的对象:System.ServiceModel.Activation.HttpModuleSystem. ServiceModel.Activation.HttpHandler

一、通过HttpModule实现服务寄宿

  在 默认的情况下,基于IIS的服务寄宿是通过一个特殊的HttpModule实现的,其类型为 System.ServiceModel.Activation.HttpModule,是一个定义在System.ServiceModel程序集中的 内部类型。HttpModule的定义大体上如下面的代码所示,我们很清楚地看到其实现的原理:将实现WCF Service请求处理的逻辑注册到HttpApplication的PostAuthenticationRequest事件中。

   1: internal class HttpModule : IHttpModule
   2: {
   3:     //其他成员
   4:     public void Init(HttpApplication context)
   5:     {
   6:         context.PostAuthenticateRequest += new EventHandler(HttpModule.ProcessRequest);
   7:     }
   8:     private static void ProcessRequest(object sender, EventArgs e)
   9:     {
  10:         //服务请求处理实现
  11:     }
  12: }

  System.ServiceModel.Activation.HttpModule是一个特殊的HttpModule,说它特别是因为当 HttpModule注册到HttpApplication的PostAuthenticateRequest事件处理程序执行后,不会再将请求进一步分 发给后续的请求处理步骤。换句话说,就HttpApplication从BeginRequest到EndRequest整个请求处理的生命周期来说,对 于基于.svc文件的请求仅仅延续到PostAuthenticateRequest阶段。我们可以通过一种简单的方式来证明这一点。

  假设我们有一个WCF服务需要通过IIS进行寄宿,并把WCF服务相应的.svc文件定义在一个对应于某个IIS虚拟目录的ASP.NET Website中。现在我们为之添加一个global.asax,在该global.asax,我通过如下的代码注册了HttpApplication处 理请求的前三个事件:BeginRequest、AuthenticateRequest和PostAuthenticateRequest,当这3个事 件触发后,将一段代表当前事件的名称写入EventLog中。

   1: <%@ Application Language="C#" %>
   2: <%@ Import Namespace= "System.Diagnostics"%>
   3: <script runat="server">
   4:    
   5:     void Application_BeginRequest(object sender, EventArgs e)
   6:     {
   7:         string message = string.Format("BeginRequest Event is raised at {0}", DateTime.Now);
   8:         EventLog.WriteEntry("Application", message, EventLogEntryType.Information);
   9:     }
  10:  
  11:     void Application_AuthenticateRequest(object sender, EventArgs e)
  12:     {   
  13:         string message =  string.Format("AuthenticateRequst Event is raised at {0}",DateTime.Now);
  14:         EventLog.WriteEntry("Application", message, EventLogEntryType.Information);
  15:     }
  16:  
  17:     void Application_PostAuthenticateRequest(object sender, EventArgs e)
  18:     {
  19:         string message = string.Format("PostAuthenticateRequest Event is raised at {0}", DateTime.Now);
  20:         EventLog.WriteEntry("Application", message, EventLogEntryType.Information);
  21:     }
  22: </script>

  如果我们上面的说法成立的话,只有HttpApplication的最初3个事件被触发。此外,HttpModule注册的操作会先于定义在 global.asax的Application_PostAuthenticateRequest方法执行,那么在整个服务调用过程中,只有 Application_BeginRequest和Application_AuthenticateRequest这两个方法会被执行。这一点我们可 以从EventLog得到证实。当我们通过执行案例7-2中的代表客户端应用程序后,EventLog中WindowsLog的Application分 组中,会多出两个日志项目(之前已经将日志清空),如图1所示。

通过Event Viewer查看添加的Event Log

图1 通过Event Viewer查看添加的Event Log

  日志的内容正是我们在Application_BeginRequest和Application_AuthenticateRequest方法中 定义的日志文本。可见仅仅这两个方法被成功执行,Application_PostAuthenticateRequest方法却没有被执行。可以想象, 后续的事件也不可能被触发,如图2所示。

Event Log的详细内容

图2 Event Log的详细内容

  到现在为止,我们仅仅是介绍了如何处理基于.svc文件的请求,并没有说明.svc文件对应的WCF Service是如何被寄宿的。服务的寄宿发生在对服务.svc文件的第一次访问,具体的实现很简单:ServiceMode根据请求的目的地址加载相应 的.svc文件,通过解析定义在<%ServiceHost%>指令的Factory和Service属性得到 ServiceHostFactory和Service的类型(Factory默认为 System.ServiceMode.ServiceHostFactory),通过反射创建继承自基类 System.ServiceModel.Activation.ServiceHostFactoryBase的ServiceHostFactory 对象。最后通过ServiceHostFactory创建的继承自基类System.ServiceModel.ServiceHostBase的 ServieHost对象对Serivce进行寄宿。

二、ASP.NET并行(Side by Side)模式

  对于基于IIS服务寄宿,System.ServiceModel.Activation.HttpModule将基于.svc的请求劫持并分发给 WCF的服务模型,从而结束了请求在ASP.NET管道的旅程。除了ASP.NET提供的一些少量的底层服务,比如动态编译和AppDomain管理等, 绝大部分ASP.NET对传统的ASP.NET资源的请求处理机制将不会应用在基于WCF Service的请求处理流程中。从这个意义上讲,我们可以说WCF Service的运行模式和ASP.NET运行时采用的是一种并行的模式。

  你完全可以用一个映射到某个IIS虚拟目录的ASP.NET Website同时作为asmx Web Service和.svc WCF Service的宿主。在这种情况下,ASP.NET .aspx Page、.asmx Web Service和WCF service运行在同一个AppDomain中。但是HttpRuntime对于.aspx Page和.asmx Web Service的处理机制并不会应用于对.svc WCF Service请求。我们把WCF Service这种寄宿模式称为ASP.NET并行(Side by Side)模式,图3揭示了这种寄宿模式。

ASP.NET并行模式

图3 ASP.NET并行模式

  在图3体现的这种情况下(ASP.NET .aspx Page和.svc WCF Service共存于同一个AppDomain),.aspx可以直接定位WCF Service,它们之间还可以共享一个基于AppDomain的状态,比如类型的静态属性。但是很多ASP.NET特性将不能被WCF Service使用,比如:

  • HttpContext对于WCF Service来说,HttpContext.Current永远为null;
  • 基于文件或者Url的授权:基于.svc文件的ACL(Access Control List)的授权和ASP.NET通过<authorization>定义的基于URL的授权都将失去效力。原因很简 单,System.ServiceModel.Activation.HttpModule在PostAuthenticateRequest阶段就将请 求劫持,而授权(Authorization)发生在PostAuthenticateRequest之后;
  • HttpModule扩展:作用于PostAuthenticateRequest事件后期的HttpModule将不会生效;
  • 身份模拟(Impersonation):即使通过配置<identity impersonate=”true” />允许身份模拟,WCF Service总是运行在IIS进程账号下。

不过,WCF服务模型通过自己的方式解决了上面的问题,比如:

  • OperationContext:ASP.NET HttpContext是基于当前的请求,WCF的OperationContext是基于当前的操作,本质上是一样的基于上下文的容器;
  • ServiceAuthorizationBehaviorServiceAuthorizationBehavior是一个Service行为,用于实现WCF的授权;
  • DispatchMessageInspector + 自定义Channel:DispatchMessageInspector和自定义Channel分别在服务模型和信道层对入栈消息进行额外的筛选和处理,和自定义HttpModule异曲同工;
  • 基于操作的身份模拟(Impersonation):WCF自身也提供了基于操作的身份模拟实现。

  为什么WCF要采用这种于ASP.NET并行的模式,而不像Web Service一样采用与ASP.NET完全兼容呢?这主要是因为WCF和.asmx Web Service有本质的区别:Web Service总是采用IIS寄宿,并使用HTTP作为传输,而WCF则具有不同的寄宿方式,对于传输协议的选择也没有限制。在默认的情况下,不论采用何 种寄宿方式,WCF本身的行为应该保持一致。所以,让WCF 服务的行为独立于寄宿的环境与传输协议,是采用并行模式的主要原因。

三、ASP.NET兼容模式

  虽然在默认的情况下,IIS的寄宿采用ASP.NET并行的模式。但是在一个Web应用中,尤其是一些AJAX的Web应用,却明确地需要以一种 ASP.NET兼容模式处理WCF Service请求。比如,在WCF Service的操作中,需要获取ASP.NET应用的SessionState,或者是需要通过基于.svc文件的ACL对WCF Service进行授权等。

  WCF对此提供了支持,实现起来也很简单,对于编程来说,仅仅需要在Service类型加上一个特殊的 AspNetCompatibilityRequirementsAttribute特性,并将RequirementsMode属性指定为 AspNetCompatibilityRequirementsMode.Allowed,实例代码如下:

   1: [AspNetCompatibilityRequirements(
   2: RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
   3: public class CalculatorService:ICalculator
   4: {  
   5:     //省略成员
   6: }

除此之外,WCF的配置也需要做一些修改,我们需要将<serviceHostingEnvironment/>配置节的aspNetCompatibilityEnabled属性设为true。

   1: <?xml version="1.0"?>
   2: <configuration>
   3: <system.serviceModel>    
   4:     <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
   5:     <!—其他配置-->
   6:   </system.serviceModel>
   7: </configuration>

  在ASP.NET兼容模式下,ASP.NET将会采用与处理.aspx、asmx一样的方式来处理基于.svc的请求,对WCF Service请求的处理将会贯穿HttpApplication请求处理的整个生命周期(从BeginRequest到EndRequest)。对于 ASP.NET兼容模式,System.ServiceModel. Activation.HttpModule将忽略对HttpApplication对象PostAuthenticateRequest事件的注册,原 本实现在HttpModule中对WCF Service的请求处理逻辑将被一个HttpHandler 中:System.ServiceModel.Activation.HttpHandler。如同System.Web.UI.Page(本质上是一个 HttpHandler)负责最终处理对.aspx的请求一样,System.ServiceModel.Activation.HttpHandler 服务负责最终对.svc的请求。HttpHandler是一个定义在System.ServiceModel程序集中的内部类型。HttpHandler 的定义如下,请求处理实现在ProcessRequest方法中,具体的逻辑与实现在 System.ServiceModel.Activation.HttpModule中的是完全一致的。

   1: internal class HttpHandler : IHttpHandler, IRequiresSessionState
   2: {
   3:     public HttpHandler();
   4:     public void ProcessRequest(HttpContext context);
   5:  
   6:     public bool IsReusable { get; }
   7: }
作者:Artech
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

加支付宝好友偷能量挖...


评论(0)网络
阅读(181)喜欢(1)Asp.Net/C#/WCF