Debugging an HTTP Handler (*.ashx) in ASP.NET 2.0

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

By Dina Fleet Berry

A handler is responsible for fulfilling requests from a browser. Requests that a browser manages
are either handled by file extension (or lack thereof) or by calling the handler directly. Only
one handler can be called per request. A handler does not have any HTML static text like .aspx
or .ascx files. A handler is a class that implements the IHttpHandler interface. If you need
to interact with any Session information, you will also need to implement IRequiresSessionState.
If you want to make an asynchronus handler, you will need to implement the IHttpAsyncHandler
interface instead of the IHttpHandler interface.


Calling a Handler by File Extension

A handler can be invoked by any file extension that is mapped via the web.config and the IIS
file extension mappings. You can have a file named something.billybob where ‘billybob’ is the
file extension, or you can have no file extension at all such as http://web/handler/file where
a request for ‘file’ without an extension invokes a handler mapped to *.* or the directory as *.

Calling a Handler Directory

The code file for the handler has the file extension of ‘ashx’ on the web server.
This file can be called directly via a browser without having to set up web.config
or IIS file extension mappings. For example: http://web/handler/handler.ashx.
Some examples of this hander type are photo albums, RSS feeds, and blogging sites.
Each of these is a good example of work that can be better accomplished without
standard HTML. A photo album involves directory crawling and responding with
pictures. A RSS feed returns information in the correct format.

Trace.axd as an Example Handler

An example of a handler that is invoked by file extension is the Trace.axd file used for debugging.
In order to invoke the Trace.axd handler, you configure the website for tracing by adding a trace
section to web.config:

<configuration>
    <system.web>
        <trace enabled="true"/>

    </system.web>
</configuration>

and call the trace.axd file from the root of the website such as http://localhost/trace.axd.

Required IIS Mappings for Handlers Called by File Extension

Handlers that are invoked based on a request’s file extension require configuration in web.config and in
the IIS file extension mapping. In order to see the file mapping of the *.axd file, open the IIS Manager
and configure the application. Go to the ‘Mappings’ tab and scroll down until you see the .axd file extension.

Double-click on the .axd extension in order to see more details about this mapping.

Invoking handlers by file extension or no extension gives you flexibility about what is called and when.
There are many articles on the web about file extension handlers so this article will focus on the handler
that is called directly.

Creating a Handler

In order to create a handler code file, you need a web site in Visual Studio 2005
(or Visual Studio 2003 for a .NET 1.x handler). For a new website, select ‘ASP.NET Web Site’.

This will create a web site with an App_Data directory and the default.aspx file.
You won’t use the default.aspx so you can either leave it alone or delete it.

You will need to create the web.config and the handler file by clicking on the website
in the Solution Explorer and add new items of ‘web.config’ and ‘Generic Handler’.

The handler code file will look like:


<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;

public class Handler : IHttpHandler {

    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";
        context.Response.Write("Hello World");

    }

    public bool IsReusable {
        get {
            return false;
        }
    }

}

At this point, build the project and set the handler.ashx file as the startup file.
Start debugging. You will be prompted to allow the process to change your web.config to allow
debug. Unless you want to type in the change to allow debugging yourself
("<compilation debug="true"/>"), accept this change.

In my brower, because my desktop is set-up to parse text/plain as XML, I get an error that looks like:

The handler.ashx that Visual Studio provided needs to be fixed before it will work.
So change the Content Type from ‘text/plain’ to ‘text/html’. Build, and debug again. You should
get a response that looks like:

Handler.ashx

The handler.ashx file implements the IHttpHandler interface and has two methods of
‘ProcessRequest’ and ‘IsReusable’. The ‘ProcessRequest’ is your main method where you put your
code. The ‘IsReusable’ method is set to true by default and indicates whether another request
can use the IHttpHandler instance. If the instance can be reused mark it true — this will
improve the speed of the handler, and reduce the work your server will have to do.
But if your instance should not be reused, due to state or because it is ansychronous,
you should set IsReusable to false.

Session State

Before continuing, change the handler.ashx code to implement the IRequiresSessionState
interface. If you need read-only access to the Session, implement the IReadOnlySessionState interface.
Both interfaces are in the System.Web.SessionState namespace which you will need to add with the ‘using’
syntax. The code should now look like:

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;
using System.Web.SessionState;

public class Handler : IHttpHandler , IReadOnlySessionState{

    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/html";
        context.Response.Write("Hello World");
    }

    public bool IsReusable {
        get {
            return false;
        }
    }

}

Set a break point for the first line in the class and start debugging again.

Handling Issues with a Debug Build

When you use a debug build, you have the full resources of the Visual Studio debugger, all .NET
Framework namespaces, and any system utilities and applications. The Visual Studio Debugger allows
you to view the call stack, local variables, any watch variables, etc. You are probably familiar
with using these features to some degree. However, with a HTTP Hander, debugging is much more
important because the only visual indication of a bug is whatever you code into the content.

Handling Issues with a Release Build

The release build is different from the debug build in both features and performance. When you are
using a release build, it is probably in a production environment. As such, you will need to use a
different set of tools to find your issues.

Debugging with System.Diagnostics Namespace

The Diagnostics namespace has many classes to help with debugging in release and debug mode. If you
are debugging in release mode, you should use the Trace class. If you are debugging in debug mode,
you should use the Assert class. This article doesn’t cover all the abilities of this namespace
so you should investigate what class will help you solve your immediate problem.

In order to show these classes in action, change the code to use the namespace and use an
infinite loop. The assertion is throw when the expression in the assertion is false, so
the assertion will throw when the counter is no longer less than 10.

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;
using System.Web.SessionState;

using System.Diagnostics;

public class Handler : IHttpHandler , IReadOnlySessionState{

    public void ProcessRequest (HttpContext context) {

        context.Response.ContentType = "text/html";
        context.Response.Write("Hello World");

        int i=0;

        while (1!=0)
        {
            Debug.Assert(i<10);
            i++;
        }

    }

    public bool IsReusable {
        get {
            return false;
        }
    }

}

Build and debug. Since there are no breakpoints set, the debugger will become active again when the
assertion is hit. This is great if you know what the error is you are getting and can test for it.
The first thing that happens is an assertion window pops up with information about the assertion.
If you want the debugger to become active, click the "Retry" button. This will bring up
the debugger at that assertion code line.

If you know a general code location you would like to look at instead of a specific Boolean statement
to test for, you should use the Debugger.Break statement in your code. This will pop up the debugger
as well. If you want to look at preconditions and then stop at the error, you can combine these two
statements to quicken your debugging process. An example of that code is below:

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;

using System.Web.SessionState;
using System.Diagnostics;

public class Handler : IHttpHandler , IReadOnlySessionState{

    public void ProcessRequest (HttpContext context) {

        context.Response.ContentType = "text/html";
        context.Response.Write("Hello World");

        Debugger.Break();

        int i=0;

        while (1!=0)
        {
            Debug.Assert(i<10);
            i++;
        }

    }

    public bool IsReusable {
        get {
            return false;
        }
    }

}

You can use the Debug.Write, Debug.WriteIf, Debug.WriteLine, and Debug.WriteLineIf to print to the Output
window during a debug session. This could help you view the state of the HTTP Handler.

If you want to track down an issue in a release build, you will have to have a way of peering into the code.
One way to do that is to add EventLog.WriteEntry statements into your code. This allows you to add information
to the event log as the HTTP Hander is running. The WriteEntry method is heavily overloaded so you will need
to determine the best method for your use. If you want to see information as a simple information entry in
the event viewer, you just use one String argument. The Event Viewer needs to know the "Source"
of the event so let’s name it "HTTPHandler – DebugArticle". This should be easy to see in the
Event Viewer. The code looks like:

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;
using System.Web.SessionState;
using System.Diagnostics;

public class Handler : IHttpHandler , IReadOnlySessionState{

    public void ProcessRequest (HttpContext context) {

        context.Response.ContentType = "text/html";

        context.Response.Write("Hello World");

        int i=0;

        // Create an EventLog instance and assign its source.
        EventLog myLog = new EventLog();
        myLog.Source = "HTTPHandler-DebugArticle";

        while (i<100)
        {
            if (i/10==0)
            {
                // Write an informational entry to the event log.
                myLog.WriteEntry("Writing to event log: i is divisible by 10.");
            }

            i++;
        }

    }

    public bool IsReusable {
        get {
            return false;
        }
    }

}

Build a release version of the handler and call it. The output should only include the
"Hello World" text. Open the Event Viewer, Application log. You should see
several postings from the source of "HTTP Handler – DebugArticle".

If you open one of the events, you should see a simple output:

There are also classes to interact with the Performance monitor, and processes as well as several
classes to help with logic flow.

Attaching to a Process to Debug a Running HTTP Handler

Another method of finding your issues is to attach to a process that contains the HTTP Handler.
If you are running multiple websites where each website is in it’s own process space, you may
have to figure out the process id. The easiest way is to have your handler tell you. The
System.Diagnostics namespace includes a class called Process which will help you determine
the current process’s id. In order to find the id, let’s send the id to the event viewer
by changing the code to:

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;
using System.Web.SessionState;
using System.Diagnostics;

public class Handler : IHttpHandler , IReadOnlySessionState{

    public void ProcessRequest (HttpContext context) {

        context.Response.ContentType = "text/html";
        context.Response.Write("Hello World");

        int i=0;

        Process.GetCurrentProcess().Id.ToString();

        // Create an EventLog instance and assign its source.
        EventLog myLog = new EventLog();
        myLog.Source = "HTTPHandler-DebugArticle";
        myLog.WriteEntry("Process Id is " + Process.GetCurrentProcess().Id.ToString());

        while (i<100)
        {
            if (i/10==0)
            {
                // Write an informational entry to the event log.
                myLog.WriteEntry("Writing to event log: i is divisible by 10.");
            }

            i++;
        }

    }

    public bool IsReusable {
        get {
            return false;
        }
    }

}

The important code line here is in the loop and is an alteration to the string in the
myLog.WriteEntry:Process.GetCurrentProcess().Id. The following figure shows what the
Event Viewer entry looks like:

Now that you know the process id, you need to find this id in the Task Manager. In order to see process
ids in the Task Manager, click to the Process tab then click on View and choose Select Columns.
Click on PID (Process Identifier) and OK out of the Dialogs. The list of processes should now
include the PID, which you can sort by if you click on the column name. Once you find the right
process id in the list, right click on it and choose Debug. This will start Visual Studio. You
will have to open your code file from Visual Studio and debug at that point.

Summary

An ASP.NET HTTP Handler is a simple class that allows you to process a request and return
a response to the browser. The handler gives you access to the current web context and
the session state. The default ‘Generic Hander’ that Visual Studio 2005 gives you may not
work with its current code. With a few changes, working with the System.Diagnostics namespace
will have you writing HTTP handlers that build and debug successfully.


More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read