閱讀70 返回首頁    go 技術社區[雲棲]


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: }

作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-30 14:34:22

  上一篇:go  [原創]WCF技術剖析之三:如何進行基於非HTTP的IIS服務寄宿
  下一篇:go  WCF技術剖析之五:利用ASP.NET兼容模式創建支持會話(Session)的WCF服務