HttpModule Init方法被多次调用 - 为什么?

问题描述:

我正在创建一个http模块,并且在调试时我注意到了一些起初(至少)似乎很奇怪的行为。HttpModule Init方法被多次调用 - 为什么?

当我在httpmodule的init方法中设置断点时,我可以看到http模块init方法被多次调用,即使我只启动了网站进行调试并发出一个请求(有时它只打1次,其他时间打10次)。

我知道我应该期待HttpApplication的几个实例运行,并且每个http模块都将被创建,但是当我请求单个页面时,它应该由单个http应用程序对象处理,因此只会触发事件关联一次,但它仍然会为每个请求触发事件几次,这是没有意义的 - 除了它必须在该httpApplication中添加多次 - 这意味着它是每次都调用相同的httpmodule init方法,而不是一个新的http应用程序正在创建每次它击中我的中断点(请参阅我的代码示例在底部等)。

这里可能会出现什么问题?是因为我正在调试并在http模块中设置断点?

它已经注意到,如果我启动网站进行调试并快速跨越httpmodule中的断点,它只会触发init方法一次,对于事件处理程序也是如此。如果我让它在断点处停留几秒钟,init方法会被多次调用(看起来好像取决于在跨越断点之前等待的时间)。也许这可能是一些功能的构建,以确保httpmodule已初始化,并且http应用程序可以为请求提供服务,但它似乎也可能具有灾难性后果。

这可能似乎是合乎逻辑的,因为它可能会尝试完成请求,因为我已经设置了断点就觉得某件事情出现了问题,并尝试再次调用init方法?所以它可以处理请求?

但是,这是怎么回事,是一切正常(我只是猜测),还是真正的问题?

我特别关心的是,如果有什么东西让它挂在“生产/生活”服务器上几秒钟,很多事件处理程序会通过init被添加,因此每个页面请求都会触发事件处理程序几次。

此行为可能会迅速导致任何网站停机。

我已经看过用于针对的HttpModules和formsauthentication的rolemanagermodule等“原始”的.NET代码,但我的心不是代码有什么不同,这些模块使用。

我的代码看起来像这样。

public void Init(HttpApplication app) 
    { 
     if (CommunityAuthenticationIntegration.IsEnabled) 
     { 
      FormsAuthenticationModule formsAuthModule = (FormsAuthenticationModule) app.Modules["FormsAuthentication"];   

      formsAuthModule.Authenticate += new FormsAuthenticationEventHandler(this.OnAuthenticate); 
     } 
    } 

这里有一个例子它是如何在RoleManagerModule从.NET框架

public void Init(HttpApplication app) 
    { 
     if (Roles.Enabled) 
     { 
      app.PostAuthenticateRequest += new EventHandler(this.OnEnter); 
      app.EndRequest += new EventHandler(this.OnLeave); 
     } 
    } 

不要任何人都知道是怎么回事呢?

(我只是希望有人在那里能告诉我为什么发生这种情况,并确保我一切都完美的罚款):)


UPDATE:

我试图缩小问题到目前为止,我发现被调用的Init方法总是在我的http模块的一个新对象上(我以前认为的)。

我看起来对于第一个请求(启动站点时),所有正在创建的HttpApplication对象及其模块都尝试服务于第一个请求,因此所有都碰到了正在添加的事件处理程序。 我真的不知道为什么会发生这种情况。

如果我请求另一个页面,所有创建的HttpApplication(和它们的无模块)将再次尝试服务请求,导致它多次触发事件处理程序。

但它似乎也是,如果我然后跳回到第一页(或另一个)只有一个HttpApplication将开始照顾请求和一切都如预期的 - 只要我不让它挂在一个断点。

如果我让它在断点处挂起,它会开始创建新的HttpApplication对象并开始添加HttpApplications(多于1个)来处理/处理请求(它已经处于由当前停止的HttpApplication处理的过程中在断点处)。

我猜想或希望它可能是一些智能的“幕后”方式来帮助分发和处理负载和/或错误。但我不知道。 我希望有些人可以向我保证,这是完美的,它应该如何?

+2

我在我们的HttpModule中看到了这种行为 – 2009-10-16 16:46:52

+0

John:我忘了关闭这个。它发生(至少在我的情况下),因为该模块受到浏览器对任何资源(图像,JavaScript,样式表)的每一个请求的影响。它受到打击的原因是在MVC中使用的路由模块。所有请求都需要由路由模块处理,因此所有请求都会不幸地触发模块。我曾问过这个问题,但没有人回答。 – MartinF 2009-10-27 03:32:14

  1. 检查HttpContext.Current.Request看,有什么要求模块的初始点火。可能是浏览器发送多个请求。

  2. 如果您已连接到IIS,请检查IIS日志,以确定是否收到您停留在中断点的任何请求。

+0

正确。发生这种情况是因为所有请求(对于任何资源)都会触发该模块。 在我看来,由于路由模块的情况。 – MartinF 2009-10-27 03:33:07

+0

感谢您的回复。 我不使用它们中的任何一个。全局asax中的init重写HttpApplication中的init。我的问题是我的httpmodule中的Init方法被多次调用。 据我了解,它应该为每个http应用程序创建http模块。在每个请求中,实例化的HttpApplication中的一个将处理请求,并且触发添加的事件,但由于某种原因,事件处理程序*有时会*在同一个http模块上多次添加init方法(不是新创建的for其他httpapplication的)被称为serveral时代。 – MartinF 2009-07-17 01:44:20

+0

更多阅读添加。 – 2009-07-17 02:16:35

Init()方法被多次调用是正常的。当一个应用程序启动时,ASP.NET Worker进程将实例化它认为需要的尽可能多的HttpApplication对象,然后将它们集中(例如,为新请求重新使用它们,类似于数据库连接池)。

现在对于每个HttpApplication对象,它还会实例化每个已注册的IHttpModule的一个副本,并多次调用Init方法。因此,如果创建了5个HttpApplication对象,将会创建5个IHttpModule副本,并将您的Init方法调用5次。合理?

现在为什么它实例化5 HttpApplications对象说?那么也许你的ASPX页面有链接到你的浏览器将尝试下载的其他资源的链接,CSS,JavaScript,WebResource.aspx,也许是一个iframe的地方。或者也许ASP.NET工作进程'有心情'启动超过1个HttpApplication对象,这实际上是在IIS(或VS内置的web服务器)下运行的ASP.NET进程的内部细节/优化。

如果你想有保证只运行一次(并且不希望使用在Global.asax的Application_StartUp事件)的代码,你可以尝试在你的IHttpModule如下:

private static bool HasAppStarted = false; 
private readonly static object _syncObject = new object(); 

public void Init(HttpApplication context) 
{ 
    if (!HasAppStarted) 
    { 
     lock (_syncObject) 
     { 
      if (!HasAppStarted) 
      { 
       // Run application StartUp code here 

       HasAppStarted = true; 
      } 
     } 
    } 
} 

我我做了类似的事情,似乎也行得通,尽管我很欢迎批评我的工作,以免我错过了某些东西。

上面的例子锁定了所有请求的IHttpModule,然后它将整个应用程序冻结。 如果你的IHttpModule的呼叫请求几次需要调用HttpApplication的方法CompleteRequest和处置的IHttpModule的一个HttpApplication实例在EndRequest事件,以去除的HttpApplication实例是这样的:

public class TestModule :IHttpModule 
    { 
     #region IHttpModule Members 

     public void Dispose() 
     { 

     } 

     public void Init(HttpApplication context) 
     { 
      context.BeginRequest += new EventHandler(context_BeginRequest); 
      context.EndRequest += new EventHandler(context_EndRequest); 
     } 

     void context_EndRequest(object sender, EventArgs e) 
     { 
      HttpApplication app = sender as HttpApplication; 
      app.CompleteRequest(); 
      app.Dispose(); 
     } 

     void context_BeginRequest(object sender, EventArgs e) 
     { 
      //your code here 
     } 

     #endregion 
    } 

如果你需要IHttpModule的请求每次没有rerequest回发使用上面的这段代码。