WCF技术剖析之四:基于IIS的WCF服务寄宿(Hosting)实现揭秘
原文:WCF技术剖析之四:基于IIS的WCF服务寄宿(Hosting)实现揭秘
通过《再谈IIS与ASP.NET管道》的介绍,相信读者已经对IIS和ASP.NET的请求处理管道有了一个大致的了解,在此基础上去理解基于IIS服务寄宿的实现机制就显得相对容易了。概括地说,基于IIS的服务寄宿依赖于两个重要的对象:System.ServiceModel.Activation.HttpModule和System. ServiceModel.Activation.HttpHandler。
一、通过HttpModule实现服务寄宿
在默认的情况下,基于IIS的服务寄宿是通过一个特殊的HttpModule实现的,其类型为System.ServiceModel.Activation.HttpModule,是一个定义在System.ServiceModel程序集中的内部类型。HttpModule的定义大体上如下面的代码所示,我们很清楚地看到其实现的原理:将实现WCF Service请求处理的逻辑注册到HttpApplication的PostAuthenticationRequest事件中。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
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中。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
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所示。
图1 通过Event Viewer查看添加的Event Log
日志的内容正是我们在Application_BeginRequest和Application_AuthenticateRequest方法中定义的日志文本。可见仅仅这两个方法被成功执行,Application_PostAuthenticateRequest方法却没有被执行。可以想象,后续的事件也不可能被触发,如图2所示。
图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揭示了这种寄宿模式。
图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是基于当前的操作,本质上是一样的基于上下文的容器;
- ServiceAuthorizationBehavior:ServiceAuthorizationBehavior是一个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,实例代码如下:
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: [AspNetCompatibilityRequirements(
2: RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
3: public class CalculatorService:ICalculator
4: {
5: //省略成员
6: }
除此之外,WCF的配置也需要做一些修改,我们需要将<serviceHostingEnvironment/>配置节的aspNetCompatibilityEnabled属性设为true。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
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中的是完全一致的。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: internal class HttpHandler : IHttpHandler, IRequiresSessionState
2: {
3: public HttpHandler();
4: public void ProcessRequest(HttpContext context);
5:
6: public bool IsReusable { get; }
7: }
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
WCF技术剖析之四:基于IIS的WCF服务寄宿(Hosting)实现揭秘的更多相关文章
- WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序)
原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序) 基于HTTP-GET的元数据发布方式与基于WS-MEX原理类似,但是ServiceMetad ...
- WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序)
原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序) 通过<如何将一个服务发布成WSDL[编程篇]>的介绍我们知道了如何可以通过编程或者配 ...
- WCF技术剖析之七:如何实现WCF与EnterLib PIAB、Unity之间的集成
原文:WCF技术剖析之七:如何实现WCF与EnterLib PIAB.Unity之间的集成 在这之前,我写过深入介绍MS EnterLib PIAB的文章(参阅<MS Enterprise Li ...
- WCF技术剖析之二十七: 如何将一个服务发布成WSDL[编程篇]
原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[编程篇] 对于WCF服务端元数据架构体系来说,通过MetadataExporter将服务的终结点导出成MetadataSet(参考< ...
- WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的?
原文:WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的? 服务端只有抛出FaultException异常才能被正常地序列化成Fault消息,并实现向客户 ...
- WCF技术剖析之二十一:WCF基本异常处理模式[下篇]
原文:WCF技术剖析之二十一:WCF基本异常处理模式[下篇] 从FaultContractAttribute的定义我们可以看出,该特性可以在同一个目标对象上面多次应用(AllowMultiple = ...
- WCF技术剖析之二十一:WCF基本异常处理模式[中篇]
原文:WCF技术剖析之二十一:WCF基本异常处理模式[中篇] 通过WCF基本的异常处理模式[上篇], 我们知道了:在默认的情况下,服务端在执行某个服务操作时抛出的异常(在这里指非FaultExcept ...
- WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理
原文:WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理 在前面一片文章(服务代理不能得到及时关闭会有什么后果?)中,我们谈到及时关闭服务代理(Service Proxy)在一个高并发环境 ...
- WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用
原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...
随机推荐
- "No appenders found for logger" and "Please configure log4j properly"
Why do I see a warning about "No appenders found for logger" and "Please configure lo ...
- MFC基础类源码CPP实现文件
WinMain.CPP---->AfxWinMain() //近似可认为是WinMain()函数的入口 VIEWCORE.CPP---->CView DOCCORE.CPP----> ...
- 从零开始学C++之IO流类库(三):文件的读写、二进制文件的读写、文件随机读写
一.文件的读写 如前面所提,流的读写主要有<<, >>, get, put, read, write 等操作,ofstream 继承自ostream, ifstream 继承自 ...
- 怎么在Ubuntu Scope中获取location地址信息
Location信息对非常多有地址进行搜索的应用来说非常重要.比方对dianping这种应用来说.我们能够通过地址来获取当前位置的一些信息.在这篇文章中,我们来介绍怎样获取Scope架构中的位置信息. ...
- L型代码结构案例:Link访问权限(上)
这是松结对编程的第20篇(专栏目录). 本文探讨Link访问权限的最佳实现方法,力求外观干净且封装良好. 这些代码将位于L型代码结构(参见松结对编程系列中的定义)的下层,调用者无需理解其原理. 顺便说 ...
- Ceph之数据分布:CRUSH算法与一致性Hash
转自于:http://www.cnblogs.com/shanno/p/3958298.html?utm_source=tuicool 数据分布是分布式存储系统的一个重要部分,数据分布算法至少要考虑以 ...
- java 简单的数据增删该查
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sq ...
- 计算阶乘并显示_winform (20以后的阶乘溢出)
编写一个窗体应用程序,计算n的阶乘,显示其结果,同时,将结果显示在一个标签中. 新建窗体应用程序(如下),新建控件label1,label2,label3,textBOX1,button1,butto ...
- Python 第十三篇之一:前端页面 js和dome
一:JavaScript: JavaScript是一门编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的 ...
- java -- 轻量级锁
在了解轻量级锁之前,首先要知道什么是CAS CAS--Compare And Swap 比较并交换--通过比较传入的旧值和原内存位置中的值比较,来决定是不是要更新数据. CAS的语义是“我认为V的值应 ...