【问题标题】:How to limit HttpModule with ONLY ONE call per request?如何限制 HttpModule 每个请求只能调用一次?
【发布时间】:2011-04-01 21:01:16
【问题描述】:

这是我对 HttpModule 的实现:

带模块的文件:

public class HttpModuleRewriter : IHttpModule
{
    #region IHttpModule

    public void Init(HttpApplication app)
    {
        app.BeginRequest += ProcessRequest;
    }

    public void Dispose()
    {
    }

    #endregion

    #region Protected Methods

    protected void ProcessRequest(object sender, EventArgs e)
    {
        ...
    }
}

web.config:

<?xml version="1.0"?>
<configuration>  
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="HttpModuleRewriter" preCondition="managedHandler" type="HttpModuleRewriter" />
    </modules>
  </system.webServer>
</configuration>  

我在 HttpModuleRewriter 类的“Init”方法中设置了断点。应用程序启动时调用 1st time 方法...并且每个页面请求仅调用一次模块。

如果我对页面进行快速请求(第二个请求将在第一个请求处理之前发送),那么方法“Init”会被额外调用很少,并且对页面的每个后续请求都会导致对我的模块进行 2-3 次调用...

为什么?我该如何避免呢?

谢谢。

附:我已将公共构造函数添加到 HttpModuleRewriter 以计算引用量,在我的请求期间,我创建了 5 个模块......并且对于页面的每个请求实际上调用了 2 个模块......但仅适用于第一个导航页面,对于所有以下页面(我检查了其他 3 个)模块仅调用一次(仅调用 1 个实例)...

为什么第一页被处理了两次?建议的答案(使用“初始化”标志)也无济于事。

【问题讨论】:

    标签: c# asp.net httpmodule


    【解决方案1】:

    如果 Init() 在第二个请求到来之前还没有完成,那么你的 HttpModule 还没有准备好。如果您的 Init() 方法中有代码应该只运行一次,那么您可以设置一个标志(布尔初始化)并使用锁来防止代码被多个线程运行,例如:

    private static bool initialised;
    private static object lockObject = new object();
    
    public void Init(HttpApplication app)
    {
        lock(lockObject)
        {
             if(!initialised)
             {
               app.BeginRequest += ProcessRequest;
               //... other code here
               initialised = true;
             }
        }
    }
    

    更新: 正如this article 解释的那样,ASP.NET 可能会创建多个 HttpModule 实例,因此可以多次调用 Init()。这是设计使然。因此,您必须对模块进行调整,以便只运行一次的代码只运行一次 - 通过应用锁定,就像上面一样。

    【讨论】:

    • 那么,你认为模块被初始化两次或多次是可以的吗? ......也许你是对的。谢谢。
    • 但我想,你应该使用静态变量作为“初始化”字段。
    • 如果没有其他可用的模块,asp.net 应用程序似乎会创建一个新模块。如果您没有将当前创建的模块的 ProcessRequest 分配给 app.BeginRequest,那么后续调用模块的尝试将不会按预期调用“ProcessRequest”方法......因此实际上不会附加模块。建议的解决方案似乎不可行。
    • 嗨,是的,成员变量应该是静态的。我已经更新了答案以反映这一点。
    • 如果您像这样实现锁定,那么您的事件处理程序将在处理任何请求之前注册。
    【解决方案2】:

    我会说显而易见的答案是您的处理程序正在处理多个请求,可能是样式表或图像。

    将以下内容添加到您的 ProcessRequest 事件处理程序中,并向 context.Request.PhysicalPath 添加一个监视以确认这一点。

    HttpApplication application = (HttpApplication)sender;
    HttpContext context = application.Context;
    
    string filename = Path.GetFileName(context.Request.PhysicalPath);
    

    如果您不希望处理程序针对图像等请求运行,您需要做的就是检查以“.aspx”结尾的路径或类似的东西。

    【讨论】:

      【解决方案3】:

      当您执行 Init() 两次时,BeginRequest 事件将调用两次您的处理,因为它有两个事件处理程序。 += 运算符将新的事件处理程序添加到列表中,它不会替换旧的处理程序。

      Øyvind 有一个正确的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-14
        相关资源
        最近更新 更多