IHttpModule.BeginRequest触发了2次,Application_BeginRequest只触发了1次

6
我正在使用VS 2008和.NET 3.5 SP1。
我想在我的ASP.NET应用程序中的HttpModule中实现点击跟踪。我认为这很简单。然而,每个页面点击都会触发我的HttpModule的BeginRequest事件两次。现在网站非常简单...没有安全性,只有一点数据库工作。应该记录每个页面点击一行。为什么这个事件会触发两次呢?
此外,当第一次运行(从关闭的Web浏览器)时,IHttpModule.BeginRequest实际上会对第一个页面点击触发不同数量的事件...当我正在访问DB以为页面提供动态数据时,它会触发3次,而对于没有访问DB的页面,只会触发1次。对于第一个页面点击后的每个页面点击,无论我是否接触到DB,它都会触发2次。
值得注意的是,Application_BeginRequest(在Global.asax中)总是只会触发一次。
以下是代码:
using System;
using System.Data;
using System.Data.Common;
using System.Net;
using System.Web;
using BluHeron.BusinessLayer;
using Microsoft.Practices.EnterpriseLibrary.Data.Sql;

namespace BluHeron.HttpModules
{
    public class SiteUsageModule : IHttpModule
    {
        public void Init(HttpApplication httpApp)
        {
            httpApp.BeginRequest += OnBeginRequest;
        }

        static void OnBeginRequest(object sender, EventArgs a)
        {
            UsageLogger.LogSiteUsage(((HttpApplication)sender).Context.Request);
        }

        public void Dispose()
        { }
    }

    public static class UsageLogger
    {
        public static void LogSiteUsage(HttpRequest r)
        {
            string ipAddress = GetHostAddress(Dns.GetHostAddresses(Dns.GetHostName()));
            string browserVersion = r.Browser.Type;

            string[] urlChunks = r.RawUrl.Split('/');
            string page = urlChunks[urlChunks.GetLength(0)-1];

            SqlDatabase db = new SqlDatabase(Common.GetConnectionString());
            DbCommand cmd = db.GetStoredProcCommand("LogUsage");

            db.AddInParameter(cmd, "IPAddress", SqlDbType.NVarChar, ipAddress);
            db.AddInParameter(cmd, "BrowserVersion", SqlDbType.NVarChar, browserVersion);
            db.AddInParameter(cmd, "PageName", SqlDbType.NVarChar, page);
            db.AddInParameter(cmd, "Notes", SqlDbType.NVarChar, "");

            db.ExecuteNonQuery(cmd);
        }

        private static string GetHostAddress(IPAddress[] addresses)
        {
            foreach (IPAddress ip in addresses)
            {
                if (ip.ToString().Length <= 15)
                {
                    return ip.ToString();
                }
            }

            return "";
        }
    }
}

你能否把你正在使用的HttpModule代码发布出来吗?听起来可能是事件没有正确挂钩。 - Nick Craver
7个回答

3

可能已经晚了,但对其他人可能有用。我曾经遇到过同样的问题。每个请求都会触发两次BeginRequest事件。我调试了代码并意识到第一次触发是实际资源请求,而第二次是“favicon.ico”请求的结果。在BeginRequest事件开始时,对favicon.ico请求进行简单检查可以消除方法的第二次执行。

public void Application_BeginRequest(object sender, EventArgs e) {
   HttpApplication app = (HttpApplication)sender;
   HttpContext ctx = app.Context;

   if (ctx.Request.Path == "/favicon.ico") { return; }

1

这很有趣。我从主页面中删除了对CSS文件的引用,并且根据建议,某些浏览器在HttpModule中获得了更少的重复命中,但我仍然会得到重复。我安装了6个浏览器,并且它们之间存在一些差异。

供参考,这是我为此测试插入浏览器的URL:

http://localhost/BluHeron

default.aspx被设置为起始页,并确实返回了上述URL。我正在使用HttpRequest.RawUrl来报告用户访问的页面。具体来说,我正在拆分RawUrl字符串,并仅报告字符串数组中的最后一项(请参见代码)。

  • 每个浏览器都报告访问了default.aspx,如预期所示(RawUrl = /BluHeron/default.aspx)。
  • 6个浏览器中有4个也报告了BluHeron(RawUrl = /BluHeron)。
  • 6个浏览器中有3个还在数据库中记录了一个空白(RawUrl = /BluHeron/)。

有几种方法可以准确地报告有多少人访问了哪些页面。

  1. 仅从数据库中选择实际列出我的页面的行(忽略/BluHeron和空白)
  2. 只需在global.asax文件中使用Application_BeginRequest,这似乎每次页面访问时都会被调用。
  3. 解决这个问题。

所以,即使数据库中有垃圾数据,我也有获取良好报告的选项。我更希望了解这里发生了什么,而不是在数据库中有垃圾数据。

谢谢大家的关注!


1
"IIS的默认文档"似乎会触发第二个"BeginRequest"事件。
如果您已确定在两个事件处理程序中,HttpApplicationRequest.Path相同,并且您的URL以斜杠结尾,请尝试添加一个URL Rewrite规则以快捷处理"默认文档"。"

1

可能有点晚了,但我们也遇到了同样的问题。在我们的情况下,这是由于匿名请求首先返回 RFC 规定的 401 错误。第二个请求进行身份验证。


0
我们通过使用来解决这个问题。
HttpContext.Current.ApplicationInstance.CompleteRequest();

这应该防止您看到的两次触发。


0

有可能是您没有考虑到其他请求。例如,假设您的ASPX页面引用了一些图像或CSS文件。如果这些请求通过ASP.NET管道,则会调用您的模块并将其注册为命中。

此外,当您说IHttpModule.BeginRequest时,您是否意味着在IHttpModule.Init()中连接了HttpApplication.BeginRequest?如果是这样,那么我上面提到的原因仍然可能适用。


1
你可能想要添加的另一个常见示例是:ScriptResource.axdWebResource.axd - Nick Craver
Init() 只被调用一次。BeginRequest() 被多次调用。我得想想还有什么原因会导致额外的调用。谢谢。 - birdus
@birdus - 你的意思是只需要初始化一次吗?如果是的话,那就是应用程序初始化,只在应用程序启动时运行一次...没有特定请求,应该只运行一次。 - Nick Craver
没错,尼克。那个 Init(请见上面的代码)只会被调用一次。因此,我的事件处理程序(OnBeginRequest)只订阅了一次事件,这就排除了多次调用的原因。 - birdus

-1

在 Visual Studio 2013 及更高版本中禁用 浏览器链接,这会导致第二个请求。

VS Snapshot

这种情况发生在从Visual Studio运行应用程序时。


很明显,一个在VS 2013中新增的功能不可能在VS 2008中引起问题,因此这几乎肯定不是应该发布此答案的正确问题。 - Nathan Tuggy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接