Reputation: 7504
I'm running VS 2008 and .NET 3.5 SP1.
I want to implement hit tracking in an HttpModule
in my ASP.NET app. Pretty simple, I thought. However, the BeginRequest
event of my HttpModule
is firing twice for each page hit. The site is very simple right now...no security, just a bit of database work. Should log one row per page hit. Why is this event firing twice?
Moreover, IHttpModule.BeginRequest
actually fires a different number of times for the first page hit when running for the first time (from a closed web browser)...3 times when I'm hitting the DB to provide dynamic data for the page, and only 1 time for pages where the DB isn't hit. It fires 2 times for every page hit after the first one, regardless of whether or not I'm touching the DB.
It's interesting to note that Application_BeginRequest
(in Global.asax
) is always firing only once.
Here's the code:
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 "";
}
}
}
Upvotes: 6
Views: 5477
Reputation: 25704
One possibility is that there are other requests going on that you might not be considering. For example, let's say your ASPX page references some images or CSS files. If those requests go through the ASP.NET pipeline then your module will be called and they'll register as hits.
Also, when you say IHttpModule.BeginRequest
, do you mean that in IHttpModule.Init()
you are hooking up HttpApplication.BeginRequest
? If so then the reason I mention above might still apply.
Upvotes: 0
Reputation: 7504
This is interesting. I removed the reference to the CSS file from the master page and I'm getting fewer repeat hits in the HttpModule
for certain browsers (as was suggested), but I'm still getting repeats. I have 6 browsers installed, and I'm getting some variation between them.
For reference, this is the URL I'm plugging in to my browsers for this test:
http://localhost/BluHeron
default.aspx is set as the start page and is indeed returned for the aforementioned URL. I'm using HttpRequest.RawUrl
for reporting which page the user hit. Specifically, I'm splitting the RawUrl
string and just reporting the last item in the array of strings (see code).
RawUrl = /BluHeron/default.aspx
).RawUrl = /BluHeron
).RawUrl = /BluHeron/
).There are a couple ways I can get accurate reporting of how many people are hitting which pages.
/BluHeron
and blanks)Application_BeginRequest
in the global.asax file, which seems to consistently get called only once per page hit.So, I've got options for getting good reports even with crappy data in the database. I would prefer to understand what's going on here and not to have junk in the database.
Thanks for looking, everyone!
Upvotes: 1
Reputation: 9323
The "Default Document" part of IIS seems to fire a second BeginRequest
event.
If you have determined that the Request.Path
is the same for the HttpApplication
in both event handlers and your URL ends with a slash, try adding a URL Rewrite rule to shortcut the "Default Document" processing.
Upvotes: 1
Reputation: 11
Disable Browser Link in Visual Studio 2013 and up, which causes the second request.
This occurs when an Application is run from Visual Studio.
Upvotes: -1
Reputation: 4873
We solved this by using
HttpContext.Current.ApplicationInstance.CompleteRequest();
This should prevent the the twice fire you are seeing.
Upvotes: 1
Reputation: 61
quite late on this, but ran into the same issue. In our case it was due to the anonymous request first that returns the 401 per the RFC. The second request authenticates.
Upvotes: 1
Reputation: 31
This might be too late for the answer but can be useful for someone else. I faced with the same problem. BeginRequest event triggered for twice for each request. I debugged the code and realized that the first trigger for actual resource request but the second is result of "favicon.ico" request. At the beginning of BeginRequest event, a simple check for favicon.ico request eliminates second execution of the method.
public void Application_BeginRequest(object sender, EventArgs e) {
HttpApplication app = (HttpApplication)sender;
HttpContext ctx = app.Context;
if (ctx.Request.Path == "/favicon.ico") { return; }
Upvotes: 3