• WCF技术剖析之四:基于IIS的WCF服务寄宿(Hosting)实现揭秘


    通过《再谈IIS与ASP.NET管道》的介绍,相信读者已经对IIS和ASP.NET的请求处理管道有了一个大致的了解,在此基础上去理解基于IIS服务寄宿的实现机制就显得相对容易了。概括地说,基于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所示。

    clip_image002 

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

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

    clip_image004 

    图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揭示了这种寄宿模式。

    clip_image006 

    图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
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    去除 .sh 文件的
    Shell Notepad++导致报错:#!/bin/bash: 坏的解释器: 没有那个文件或目录
    linux 不同格式的软件包的安装
    linux:jenkins 默认安装目录
    使用nsis制作的安装包安装后,无法卸载干净的问题
    bash: ./t.sh:/bin/bash^M:损坏的解释器: 没有那个文件或目录
    jenkins启动/重启/停止命令 改端口
    解决windows系统80端口被占用问题
    find ./ -name *.bak | xargs rm -rf
    Linux查看端口占用情况,释放占用的端口
  • 原文地址:https://www.cnblogs.com/artech/p/1510497.html
Copyright © 2020-2023  润新知