Santry Technology Solutions, Content Management, DotNetNuke, SharePoint Consulting
Register | Login
Tuesday, December 02, 2008

Sections
  
About Us
  
Partners
Downloads
  
 WWWCoder.com Resource Directory

Building an Email Tracking System
1/21/2004 1:19:05 PM

In this article I will walk you through building an email tracking system by utilizing a custom http handler in .NET to handle the tracking and redirection, as well as using a small CountryLookUp class to track down the user's origin

The Task

For many portal websites, they always host one or two newsletters for their users. Oftentimes, when they send out their newsletter, they want to be able know how many users have opened and read the newsletter and where they are from.

The Solution

To accomplish this task, we embed an image call into the newsletter’s HTML so that every time when the user opens the newsletter, an image call is made and the server is notified. On the server side, we set up an HttpHandler to parse the image call in order to collect the information from the user as well as to end the call by generating a transparent beacon image.

Step 1. Decide What to Embed In the Image Call

In our example we will embed the NewsletterID and UserID into the image call, and a typical beacon image call in our example will be like:
<img src=”http://the-portal-site/Newsletter/NewsletterID.UserID.open” height=1 width=1>

So if we have NewsletterID as 234 and UserID as 123, the image tag will be:
<img src=”http://the-portal-site/Newsletter/234.123.open” height=1 width=1>

If you notice, here we use the extension ‘.open’. This is to make ourselves convenient when setting up server-side URL mapping (see step 5).

Step 2. Create an Http Handler by Implementing IHttpHandler Interface

The IHttpHandler interface defines the contract that ASP.NET implements to synchronously process HTTP Web requests using custom HTTP handlers. By implementing the IHTTPHandler interface, our custom handler will be able to process the incoming request just like ISAPI extension filters but with a simpler programming model.

using System;
using System.Web;
namespace EmailTracking { /// <summary> /// Summary description for OpenHandler. /// </summary> public class OpenHandler : IHttpHandler { // Specify the location of the beacon image private string pixelFile = "~/Images/pixel.gif"; // Constructor public OpenHandler() {} // Process the request public void ProcessRequest(HttpContext context) { OpenURIParser.Process( context.Request.Path, context.Request["REMOTE_ADDR"]); // Output image context.Response.ContentType = "image/gif"; context.Response.WriteFile( context.Request.MapPath( pixelFile ) ); context.Response.End(); }
// This property is used to determine whether an instance // of the handler can be reused across multiple requests. public bool IsReusable {
get { return true; } } } }
 
Step 3. Create a URL Parser Class

This is just a simple parsing class to get the NewsletterID, UserID and IP information from the request. We can then pass the information along to the next layer (database or file processing to record the information).
using System;
using System.IO;

namespace EmailTracking { /// <summary> /// Summary description for OpenURIParser. /// </summary> public class OpenURIParser { // Constructor public OpenURIParser() {} // Do the parsing public static void Process( string pUri, string pIP) { string[] arrIDs; string country; // Create a CountryLookUp instance by specifying // the location of the GeoIP database file CountryLookUp countryLookUp = new CountryLookUp(@"C:\winnt\system32\GeoIP.dat"); try { // a valid call will be like: // "/NewsletterID.UserID.open" arrIDs = GetReqeustedFileName(pUri).Split('.'); if (arrIDs.Length == 2) { // Get the NewsletterID int newsletterID = Convert.ToInt32(arrIDs[0]); // Get the UserID int userID = Convert.ToInt32(arrIDs[1]); // Get the user’s origin by his IP country = CountryLookUp. lookupCountryCode(pIP); // Write open stats either to database or file // You need to write your own Stats class Stats.UpdateOpenStats( newsletterID, userID, countryByIP); } } catch (Exception) { // Failure in parsing request // You can write your error handling here } } // Get the file name from the request url private static string GetReqeustedFileName(string pUri) { return Path.GetFileNameWithoutExtension(pUri); } } }
Step 4. Create a CountryLookUp Class

By utilizing MaxMind’s GeoIP database, we can obtain the Country (as mentioned on their site, they can obtain Region, City, Latitude, and Longitude as well) of any IP address. This will be very helpful if we want to geo-target newsletters.

The C# API which I have ported can be downloaded at http://www.maxmind.com/app/csharp.

using System;
using System.IO;
using System.Net;

namespace EmailTracking { /// <summary> /// Summary description for CountryLookup. /// </summary> public class CountryLookup {
private FileStream fileInput; private static long COUNTRY_BEGIN = 16776960; ....
public CountryLookup(string fileName) { fileInput = new FileStream( fileName, FileMode.Open, FileAccess.Read); }
public string lookupCountryCode(string str) { IPAddress addr; try {
addr = IPAddress.Parse(str); }
catch (FormatException e) {
return "--"; }
return lookupCountryCode(addr);
}
....
public string lookupCountryCode(IPAddress addr) { ....
}

....
} }
Step 5. Register Your Custom Http Handler

Once you have your custom handler application compiled, all you are left to do is to register your handler with IIS and ASP.NET runtime.

First, add the following entries to your appplication’s web.config file:
<system.web>
   <httpHandlers>
      <add verb="*" path="*.open"
         type="EmailTracking. OpenHandler, EmailTracking" />
      </httpHandlers>
</system.web>
This configuration tells the ASP.NET to handle requests with extension .open using our custom http handler.

The next step is to map the .open extension on IIS. Because IIS is the first place the user’s request will go, we need to config IIS to pass the request with .open extension to ASP.NET so then the request can reach our http handler.
Open the IIS snap-in and go to the application where you set up to host the tracking system and open the properties of this application. On the Directory tab, click the Configuration.

You can add the new extension to ‘.open’ as following:

Conclusion

You can see by utilizing a custom built http handler, it is so easy to build an email tracking system without much hassle. And these classes can be further extended to handle click tracking and user profiling. And as to my experience with a million-impression tracking system built on top of these classes and SQL Server, the http handler handles requests very efficiently with a very low CPU and memory usage.

Download the code!


About the Author:
Ting Huang, a senior programmer for a fast growing Minnesota based online marketing company and spends most of his time deep in system architect, ASP.NET and SQL Server.

 

Related Articles
   Related Document Sending Email from your .Net Application


Page Options:
format for printing  Format for Printer
email article  Email Page
add to your favorites   Add to Favorites
How would you rate the quality of this content?
Poor - - Excellent
Comments?
Overall Rating:
Comments Left:
Left on 8/20/2008 11:26:11 AM by Anonymous
Comments: I got this to work for email services like gmail, but i can't seem to get this to work for the Outlook client application, do  I need to change the img src for Outlook?
Left on 4/29/2008 10:14:07 PM by Anonymous
Comments:
No ratings available.
Left on 1/31/2008 4:59:33 PM by Anonymous
Comments: For those of you who want to know why a simple URL with a query string wont work, is because most Email(sorry) clients will block these images by default. This is why he is taking the "The parameters are the filename" route. Since the file doesn't exist, he maps the .open to the actual handler.

Complicated but necessary.

Way to go, buddy!

CptBongue
No ratings available.
Left on 1/31/2008 4:58:40 PM by Anonymous
Comments: For those of you who want to know why a simple URL with a query string wont work, is because most web clients will block these images by default. This is why he is taking the "The parameters are the filename" route. Since the file doesn't exist, he maps the .open to the actual handler.

Complicated but necessary.

Way to go, buddy!

CptBongue
Left on 8/10/2006 6:33:01 AM by Anonymous
Comments: How to implement an Email Tracking System using ASP instead using .NET. Can anyone help me email me at berryxian@yahoo.com
No ratings available.
Left on 8/15/2005 11:45:11 PM by Anonymous
Comments: well steps definition..
No ratings available.
Left on 12/16/2004 4:48:01 AM by Anonymous
Comments: its good, and help me a lot. Thanks a lot
Left on 10/15/2004 10:25:20 AM by Anonymous
Comments: if somebody has the code could you please email anthony@one.ie coz the download link is broken. i have some other code and tutorials relating to email tracking i would be happy to share
No ratings available.
Left on 10/3/2004 7:04:56 PM by Anonymous
Comments: Hi,
The "Download the code"  link doesn't seem to be working. Please check. Thanks !
No ratings available.
Left on 7/6/2004 11:43:01 PM by Anonymous
Comments: try to add map too
Left on 6/11/2004 1:20:14 PM by Anonymous
Comments: Overkill - do you wnat the app to break?  If you are dealing with 100 views thats fine - if you move to a real model of 100K+ then you need a system that works- this model is right on!
Left on 5/10/2004 7:06:49 AM by Anonymous
Comments: Pls i am new to asp.NET . Can i have have a sample webclient that can consume this service

M. Tasker
taskerng@yahoo.com
Left on 2/22/2004 8:44:33 AM by Robin Hillum
Comments: I do feel it somewhat over the top.
Why not post a url with the query string . But still a well explained resource for more complicated requirements.
Left on 2/7/2004 11:27:51 PM by Anonymous
Comments: He could have been more descriptive.
Left on 2/6/2004 12:46:44 AM by Ting Huang
Comments: Thanks to Patrick Santry! Now I have the code available for download. Please check the above "Download the code!" link after the conclusion section.
No ratings available.
Left on 2/6/2004 12:21:21 AM by Anonymous
Comments: For those of you looking for a simple solution just use an aspx page. The source attribute for an embedded image in the email might look like this: http://www.yoursite.com/stats.aspx?userid=333&emailid=333. Then all you do is call the Request.QueryString method on the aspx page and extract the variables. If you want to go further simply use the Request.ServerVariables method and you can save all the information that is contained in the http request. Note: make sure to set the Response.ContentType = “image/gif” before sending out he response. Having said this … the country lookup is very cool …
Left on 2/4/2004 6:39:45 AM by Anonymous
Comments: Comments from the following blog: Scott Galloway's Personal Blog, located at: http://www.mostlylucid.co.uk/posts/793.aspx
No ratings available.
Left on 2/4/2004 3:10:39 AM by Ting Huang
Comments: "will a user's Hotmail image settings disable this?" -- yes, so you might want to add a click tracking instead.
No ratings available.
Left on 2/4/2004 3:03:47 AM by Ting Huang
Comments: "a bit of an overkill" -- either way the server has to deal with the request; and a httphandler enables a customizable link for tracking.

"files to download" -- wish i can zip them and put them on this web site...
No ratings available.
Left on 2/3/2004 6:17:06 PM by Anonymous
Comments: Could you add the files for download?
Left on 2/3/2004 12:49:30 PM by Anonymous
Comments: Nice, but will a user's Hotmail image settings disable this?  (ie, whne a user sets images to NOT display by default in a message). - paul
No ratings available.
Left on 2/3/2004 11:49:41 AM by Anonymous
Comments: Countrylookup is Cool!
Left on 2/3/2004 9:04:23 AM by Anonymous
Comments: I recommend calling each person and asking them about the site - screw this programming crap!
Left on 2/3/2004 6:35:21 AM by Anonymous
Comments: I kinda think implementing a HTTPHandler is a bit of an overkill: Why not just use a regular asxp-page as the source of the image in the email, and then pass the parameters needed in the querystring?
Left on 2/3/2004 2:27:45 AM by Anonymous
Comments: Keep up the great work!
Left on 1/27/2004 10:24:39 PM by Anonymous
Comments: Comments from the following blog: Patrick Santry's Blog, located at: http://blogs.wwwcoder.com/psantry/archive/0001/01/01/208.aspx
No ratings available.
Left on 1/27/2004 9:09:46 PM by Anonymous
Comments: Great stuff!
  

 Latest Articles
  

 Latest News
  

 

Spotlight
Syndication

 


 


Digg This
 


DotNetNuke Platinum Benefactor

  
 

 Terms Of Use | Privacy Statement
 Copyright 2008 - Santry Technology Solutions, Box 172, Girard, PA 16417, (814) 774-0970