2012-09-26 82 views
8

我正在嘗試爲在WCF中實現的REST服務實現身份驗證,並且託管在Azure上。我正在使用HttpModule來處理AuthenticationRequest,PostAuthenticationRequest和EndRequest事件。如果Authorization頭部丟失或者其中包含的令牌無效,那麼在EndRequest期間,我將響應上的StatusCode設置爲401。但是,我確定EndRequest被調用兩次,並且在第二次調用時,響應已經有頭設置,導致設置StatusCode的代碼拋出異常。HttpModule EndRequest處理程序調用兩次

我向Init()添加了鎖,以確保處理程序未被註冊兩次;仍然跑了兩次。 Init()也運行了兩次,表明正在創建兩個HttpModule實例。但是,在VS調試器中使用Set Object ID似乎表明請求實際上是不同的請求。我已經在Fiddler中驗證過,從瀏覽器只有一個請求發佈到我的服務中。

如果我切換到使用global.asax路由而不是取決於WCF服務主機配置,處理程序只調用一次,一切正常。

如果我將配置添加到system.web配置部分以及Web.config中的system.webServer配置部分,處理程序只調用一次,一切正常。

所以我有緩解,但我真的不喜歡行爲,我不明白。爲什麼處理程序被調用兩次?

這是問題的一個最小的攝製:

Web.config文件:

<system.web> 
    <compilation debug="true" targetFramework="4.0" /> 
    <!--<httpModules> 
     <add name="AuthModule" type="TestWCFRole.AuthModule, TestWCFRole"/> 
    </httpModules>--> 
    </system.web> 
    <system.serviceModel> 
    <behaviors> 
     <endpointBehaviors> 
     <behavior name="WebBehavior"> 
      <webHttp/> 
     </behavior> 
     </endpointBehaviors> 
     <serviceBehaviors> 
     <behavior> 
      <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --> 
      <serviceMetadata httpGetEnabled="true" /> 
      <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> 
      <serviceDebug includeExceptionDetailInFaults="true"/> 
     </behavior> 
     </serviceBehaviors> 
    </behaviors> 
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" /> 
    <services> 
     <service name="TestWCFRole.Service1"> 
     <endpoint binding="webHttpBinding" name="RestEndpoint" contract="TestWCFRole.IService1" bindingConfiguration="HttpSecurityBinding" behaviorConfiguration="WebBehavior"/> 
     <host> 
      <baseAddresses> 
      <add baseAddress="http://localhost/" /> 
      </baseAddresses> 
     </host> 
     </service> 
    </services> 
    <standardEndpoints> 
     <webHttpEndpoint> 
     <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/> 
     </webHttpEndpoint> 
    </standardEndpoints> 
    <bindings> 
     <webHttpBinding> 
     <binding name="HttpSecurityBinding" > 
      <security mode="None" /> 
     </binding> 
     </webHttpBinding> 
    </bindings> 
    </system.serviceModel> 
    <system.webServer> 
    <modules runAllManagedModulesForAllRequests="true"> 
     <add name="AuthModule" type="TestWCFRole.AuthModule, TestWCFRole"/> 
    </modules> 
    <directoryBrowse enabled="true"/> 
    </system.webServer> 

的HTTP模塊:

using System; 
using System.Web; 

namespace TestWCFRole 
{ 
    public class AuthModule : IHttpModule 
    { 
     /// <summary> 
     /// You will need to configure this module in the web.config file of your 
     /// web and register it with IIS before being able to use it. For more information 
     /// see the following link: http://go.microsoft.com/?linkid=8101007 
     /// </summary> 
     #region IHttpModule Members 

     public void Dispose() 
     { 
      //clean-up code here. 
     } 

     public void Init(HttpApplication context) 
     { 
      // Below is an example of how you can handle LogRequest event and provide 
      // custom logging implementation for it 
      context.EndRequest += new EventHandler(OnEndRequest); 
     } 

     #endregion 

     public void OnEndRequest(Object source, EventArgs e) 
     { 
      HttpContext.Current.Response.StatusCode = 401; 
     } 
    } 
} 
+0

你在你的應用程序中使用UrlRewrite?看來,它會導致EndRequest發起兩次。 –

+0

它可以在後臺啓用嗎?我的web.config中沒有UrlRewriteModule。 –

+0

我不這麼認爲。這真的很奇怪,因爲我不能說我所有的問題都是由UrlRewriteModule引起的。但其中一些是。 –

回答

2

抱歉不知道爲什麼它可以被調用兩次,但EndRequest最終可能因多種原因被調用。請求完成,請求被中止,發生了一些錯誤。所以我不會相信假設如果你到達那裏,你實際上有一個401,這可能是由於其他原因。

我只是把我的邏輯在AuthenticateRequest管道:

public class AuthenticationModule : IHttpModule 
    { 
     public void Dispose() { } 

     public void Init(HttpApplication context) 
     { 
      context.AuthenticateRequest += Authenticate; 
     } 

     public static void Authenticate(object sender, EventArgs e) 
     { 
      // authentication logic here    
      //............. 

      if (authenticated) { 
       HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(myUser, myRoles); 
      } 

      // failure logic here   
      //.............   
     } 
    } 
+0

是的,我不明白爲什麼我沒有意識到我應該如何處理它(我想我一定認爲我使用的示例是「正確」),但是我最終將邏輯移入了AuthenticateRequest ,PostAuthenticateRequest和AuthorizeRequest部分。不幸的是,這並沒有回答處理程序爲什麼會這樣的問題。 –

7

當ASP.net應用程序啓動時,最大限度地提高性能ASP.NET Worker進程將根據需要實例化多個對象HttpApplication。每個HttpApplication對象也將實例化已註冊的每個IHttpModule的一個副本並調用Init方法!這實際上是在IIS下運行的ASP.NET進程的內部設計(或者cassini,它是VS在web服務器中構建的)。可能是因爲你的ASPX頁面有你的瀏覽器將嘗試下載的其他資源的鏈接,一個外部資源,iframe,一個css文件,或者ASP.NET Worker Process行爲。

幸運的是,它不是案例的Global.asax:

Here's from MSDN

的的Application_Start和Application_End方法是特殊的方法 不表示HttpApplication的事件。 ASP.NET在應用程序域的生命週期內調用它們一次 ,而不是每個 HttpApplication實例。

然而,所有的模塊已經創建

第一次ASP.NET頁面或請求在 應用過程後HTTPModule's init方法爲HttpApplication類的每個實例調用一次,一個新的HttpApplication的實例被創建。但是,爲了最大限度地提高性能,可以重複使用HttpApplication實例的多個請求。

並通過如下圖所示: enter image description here

如果你想有保證只運行一次代碼,您可以使用Global.asaxApplication_Start或設置一個標誌和基礎模塊中鎖定其中爲認證起見,不認爲是一種好的做法!

+0

我只想設置'runAllManagedModulesForAllRequests =「true」'會導致執行自定義模塊的每個請求,包括靜態文件(JS/CSS /圖像)。所以對於單個頁面加載只需要兩次,對於我來說這個設置看起來就太少了。 – astaykov

+0

我不需要代碼來運行一次;問題是我看到兩個EndRequests對於服務的單個請求。它不應該是JS/CSS/Images,因爲這是一個服務,而不是一個網頁。我可以在Fiddler看到只有一個請求正在進行。問題是爲什麼我通過Fiddler看到一個單一的請求,但是模塊中有兩個請求,以及爲什麼如果我在web.config中複製配置,這個問題就會消失。 –

+0

通常情況下,你不需要重複配置,如果你使用的是IIS7 +,配置必須在'system.webServer'節中設置!對於單個請求調用兩次「EndRequest」時,模塊的Init方法是否被調用過一次?因爲每個請求無法調用兩次EndRequest!但是我注意到你在你的WCF主機配置中啓用了'aspNetCompatibilityEnabled',你測試了什麼web服務器? IIS? IIS Express? VS Web服務器?你如何在IIS中託管你的WCF?一個'WebServiceHost'? '.svc'文件?你可以在EndRequest中發佈'HttpContext.Current.Request.Path'嗎? –

相關問題