簡體   English   中英

HttpModule EndRequest處理程序調用兩次

[英]HttpModule EndRequest handler called twice

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

我向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;
        }
    }
}

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

幸運的是,Global.asax的情況並非如此:

這是來自MSDN

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

但是,在創建所有模塊之后,為HttpApplication類的每個實例調用一次HTTPModule's init方法

第一次在應用程序中請求ASP.NET頁面或進程時,會創建一個新的HttpApplication實例。 但是,為了最大限度地提高性能,HttpApplication實例可能會被重用於多個請求。

並通過下圖說明: 在此輸入圖像描述

如果您希望保證只運行一次的代碼,您可以使用Global.asax Application_Start或設置一個標志並將其鎖定在底層模塊中,這對於身份驗證來說並不是一個好習慣!

對不起,為什么它可以被調用兩次沒有任何線索,但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           
            //.............         
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM