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处理的过程中在断点处)。
我猜想或希望它可能是一些智能的“幕后”方式来帮助分发和处理负载和/或错误。但我不知道。 我希望有些人可以向我保证,这是完美的,它应该如何?
检查HttpContext.Current.Request看,有什么要求模块的初始点火。可能是浏览器发送多个请求。
如果您已连接到IIS,请检查IIS日志,以确定是否收到您停留在中断点的任何请求。
正确。发生这种情况是因为所有请求(对于任何资源)都会触发该模块。 在我看来,由于路由模块的情况。 – MartinF 2009-10-27 03:33:07
下面是关于你应该使用什么,何时以及如何工作的一些解释。 When to use Application_Start vs Init in Global.asax?
编辑:多看书
INFO: Application Instances, Application Events, and Application State in ASP.NET
感谢您的回复。 我不使用它们中的任何一个。全局asax中的init重写HttpApplication中的init。我的问题是我的httpmodule中的Init方法被多次调用。 据我了解,它应该为每个http应用程序创建http模块。在每个请求中,实例化的HttpApplication中的一个将处理请求,并且触发添加的事件,但由于某种原因,事件处理程序*有时会*在同一个http模块上多次添加init方法(不是新创建的for其他httpapplication的)被称为serveral时代。 – MartinF 2009-07-17 01:44:20
更多阅读添加。 – 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回发使用上面的这段代码。
我在我们的HttpModule中看到了这种行为 – 2009-10-16 16:46:52
John:我忘了关闭这个。它发生(至少在我的情况下),因为该模块受到浏览器对任何资源(图像,JavaScript,样式表)的每一个请求的影响。它受到打击的原因是在MVC中使用的路由模块。所有请求都需要由路由模块处理,因此所有请求都会不幸地触发模块。我曾问过这个问题,但没有人回答。 – MartinF 2009-10-27 03:32:14