Introduction
I recently started a podcast the primary resource of which is an audio file. The audio files that I produce are in various forms ranging from MP3, M4A, and WMA. And if that were as far as the problem went we could direct file access through an ASPX file that could secure the resources or do anything other resource management that we might need. But unfortunately when running a podcast that is served by iTunes you may find that a majority of your traffic doesn't even touch your site but instead goes direct to your resources. So in this case how do you secure your files or track the number of times people access the resource?
While there may be many ways to address this particular issue I decided to create a simple HttpHandler. This article will discuss what it takes to create an HttpHandler that can keep track of new and existing resources and how many people access those resources. Unlike the majority of my other articles this will not going too much into good design, project structure, etc. Instead we will take a direct approach to solving the problem. At the end of this article we will be able to track access to any file extension that we configure to be tracked. This application will be plugged in to the ASP.NET pipeline and monitor all requests. It will then save the full path to a tracked file into a database and update its access count. With this system you will be able to drop a file of a given type anywhere in your app and have it automatically tracked.
How does an HttpHandler help us?
To get the best understanding of what an HttpHandler is and what it does take a look at this MSDN article and perhaps this one too. From a high level view though an HttpHandler must conform to IHttpHandler. IHttpHandler defines a contract that is processed by the ASP.NET synchronously for any request web request. You can configure specific details for each HttpHandler such as the verb to act on and the file extension and path to watch out for as well as the type to handle the processing for the given request.
What you do inside of your custom implementation of IHttpHandler is totally up to you. In our case we want to act as a pass through that watches for a given type of file, records a request to that file, and then returns the file as normal. However, you could extend this functionality quite a bit by adding a layer of security by way of checking permission to request the file, etc. You could also use this type of HttpHandler in a shopping cart that sells digital products where you might create a key for a one time or ten time download and after that key has been used you could shut off all access to the file for that key.
Details of an HttpHandler
A large majority of functionality for an HttpHandler is specified in the configuration file for a given site. In your web config section you will have a <httpHandlers> section. This sections definition is inherited by all sub directories in your site. Given that you can have multiple web configs at different levels you have a flexible manner to assign what is processed by a given HttpHandler.
Next you have a selection of action nodes that consist of <add>, <remove>, and <clear>. The add node allows you to specify a verb and path mapping to an HttpHandler class using its "verb", "path" and "type" attributes. The remove node removes a verb/path for a given HttpHandler. The caveat here is that the remove node must EXACTLY match the node that it is asking to remove. Lastly, the clear node removes all HttpHandler mappings currently configured or inherited by a given web.config.
Implementing IHttpHandler
Implementing IHttpHandler is pretty straight forward. Create a class that inherits from IHttpHandler. Then create the two methods that IHttpHandler requires: ProcessRequest() and IsReusable().
and...
The ProcessRequest method is your entry point into your HttpHandler. This method is called to process a given request.
The IsReusable method is called to determine whether this instance of HttpHandler can be reused for fulfilling another request of the same type or not.
Steps to create a custom HttpHandler
There are essentially three steps to creating a custom HttpHandler. The first step is to create your custom HttpHandler which inherits from IHttpHandler. Then you need to configure the mapping for that HttpHandler in the webconfig under the httpHandlers section. This last point is where you might run into some confusion. Once you have your HttpHandler and web config set up correctly everything will work as expected. However, when you publish your code to your web server you will find that access to the specified file (in our case an MP3) doesn't even seem to fire the ASP.NET pipeline. What's the deal? This is appropriate. By default IIS cares not about an MP3 resource and just serves it up as it would any other resource. You will need to map a given file extension (MP3) to the ASP.NET ISAPI extension DLL (aspnet_isapi.dll) in IIS. Take a look at this MSDN article for this last step. Be aware that if you are running in IIS 7 in Integrated mode you will have a few more hurdles to jump through.
Creating our application
In order for us to use an HttpHandler we will need to first create a web site. I am going to create an ASP.NET MVC Preview 2 website, although any asp.net website will work for this particular demo. Then I am going to create two class library projects, one for my data access called DataAccess, and one to house my HttpHandler called Handlers.
Then in my web project I am going to create a local database which will be placed into the App_Data folder of the website (feel free to attach to a production database if you like). From there I am going to create a simple Files table that will have a FileID, a Name, the CreateDate, and an aggregated count stored in the ViewCount.
With that complete I can add a LINQ to SQL Classes item to my DataAccess project. I will name that My.dbml as that will make a MyDataContext for me to work with. Then in my DataAccess project I will create a new class named FileRepository. In the FileRepository I will create a method that will increment the view count of a file that I pass in. This method will attempt to get the file by name first and then increment its ViewCount. If the File doesn't exist it will attempt to save a new instance of that file. I will also have a method to get all the files that are in the system called GetAllFiles.
With that complete I can then add a reference from my website to my DataAccess and Handlers projects. The next step is to open up my HomeController (a default application is installed when you work with the ASP.NET MVC website template). And in my Index action I will add a call to the GetAllFiles method that is in my FileRepository.
Then I need to open the corresponding view for the Index action which is Index.aspx in the Views/Home directory. In there I will add a section of code to iterate through all the File objects that are passed down to the view.
Then I am going to toss an MP3 file into my web application in two different places (song.mp3 in this case). And I will create a couple of links to those two different locations in my application in the Index.aspx view.
With all of these tasks complete I can now turn my attention to building the HttpHandler. To do this create a new class in the Handlers project and name it ResourceAccessCounter. This HttpHandler is going to use the HttpContext object that is passed to it to determine what file is being requested. It will then get a handle to that file by creating a FileInfo instance based on the path that it picked up from the HttpContext. It will then check to make sure that the physical file exists. Then, because we only want to track the number of times a file is accessed rather than to secure it, we will start a try catch statement and only attempt to increment the count. However, if the count fails we still want to return the file. In the try statement we will call the IncrementViewCount method on our FileRepository. Then, regardless of if the database operation was successful or not, we will attempt to return the file by forcing the context to return it directly (thereby short circuiting the remaining processing of the ASP.NET pipeline). In previous version of .NET you could in theory call Response.End(). However, in order to actually have processing end, you now also need to call ApplicationInstance.CompleteRequest(), then you can call Response.End(). Be careful with this type of call though as it will end all processing on this request. Which means if we wanted to add another HttpHandler to the stack of things to be done to a resource it would not be called!
With our handler completed and all the moving pieces working as expected we can now go back to our web application to modify the web.config file and actually plug in our new HttpHandler.
From here we can run our application to see two links to our mp3 files.

From there, we can click on the files as much as we like and as expected we will be prompted to download the given resource.

Then we can refresh the page and we will see a list of the files that have been entered into the tracking system as well as the number of times that each file has been accessed.

Summary
In this article we learned the steps to creating an HttpHandler. In addition to the theory behind an HttpHandler we also looked at the creation of a real world application. The application that we created allowed us to track files that are not normally in the ASP.NET processing pipeline. We are now able to track access to standard binary resources. This application could be extended to track access to files of all sorts!
About Andrew Siemer
 |
I am a 33 year old, ex-Army Ranger, father of 6, geeky software engineer that loves to code, teach, and write. In my spare time (ha!) I like playing with my 6 kids, horses, and various other animals.
This author has published 29 articles on DotNetSlackers. View other articles or the complete profile here.
|
You might also be interested in the following related blog posts
Resx and BAML Resources in WPF
read more
The State of the ViKi
read more
MediaStreamSource sample for Silverlight
read more
Islands of Richness with Silverlight on an ASP.NET page
read more
ResoureProvider, ResourceManagers Relationships in ASP.NET 2.0
read more
New major version of AspLib component library
read more
CSharp FAQ : What's New in the C# 2.0 Language and Compiler
read more
Using MediaState.dll with GAC'd Add-Ins
read more
|
|
Please login to rate or to leave a comment.