How to: Create an Asynchronous HTTP Handler

Asynchronous HTTP handlers enable you to start an external process, such as a method call to a remote server, while the handler continues processing without waiting for the external process to finish. During the processing of an asynchronous HTTP handler, ASP.NET places the thread that would ordinarily be used for the external process back into the thread pool until the handler receives a callback from the external process. This can prevent thread blocking and improve performance, because only a limited number of threads can be executed at once. If a number of users request synchronous HTTP handlers that rely on external processes, the operating system can quickly run out of threads because many threads are blocked and waiting for an external process.

The following code example demonstrates an asynchronous HTTP handler that processes requests for files with the file name extension .SampleAsync within an ASP.NET application. The example shows the code for the handler, and then how to map the .SampleAsync extension to the handler in ASP.NET. Finally, the example shows how to map the .SampleAsync extension to ASP.NET in IIS, so that IIS forwards requests that end in .SampleAsync to ASP.NET.

For more information on how the ASP.NET runtime interacts with IIS, see ASP.NET Application Life Cycle Overview.

To create the HelloWorldAsyncHandler HTTP handler class

  • Create a class named HelloWorldAsyncHandler in your App_Code directory and add the following code to the class file:

    Imports Microsoft.VisualBasic
    Imports System.Web
    Imports System.Threading
    
    Public Class HelloWorldAsyncHandler
        Implements IHttpAsyncHandler
    
        Public ReadOnly Property IsReusable() As Boolean Implements System.Web.IHttpHandler.IsReusable
            Get
                Return False
            End Get
        End Property
    
        Public Function BeginProcessRequest( _
            ByVal context As System.Web.HttpContext, _
            ByVal cb As System.AsyncCallback, _
            ByVal extraData As Object) _
            As System.IAsyncResult _
            Implements System.Web.IHttpAsyncHandler.BeginProcessRequest
            context.Response.Write("<p>Begin IsThreadPoolThread is " _
                & Thread.CurrentThread.IsThreadPoolThread & "</p>" & vbCrLf)
            Dim asynch As New AsynchOperation(cb, context, extraData)
            asynch.StartAsyncWork()
            Return asynch
        End Function
    
        Public Sub EndProcessRequest(ByVal result As _
             System.IAsyncResult) _
             Implements System.Web.IHttpAsyncHandler.EndProcessRequest
        End Sub
    
        Public Sub ProcessRequest(ByVal context _
                As System.Web.HttpContext) _
                Implements System.Web.IHttpHandler.ProcessRequest
            Throw New InvalidOperationException()
        End Sub
    End Class
    
    Class AsynchOperation
        Implements IAsyncResult
        Private _completed As Boolean
        Private _state As [Object]
        Private _callback As AsyncCallback
        Private _context As HttpContext
    
        ReadOnly Property IsCompleted() As Boolean _
                Implements IAsyncResult.IsCompleted
            Get
                Return _completed
            End Get
        End Property
    
        ReadOnly Property AsyncWaitHandle() As WaitHandle _
                Implements IAsyncResult.AsyncWaitHandle
            Get
                Return Nothing
            End Get
        End Property
    
        ReadOnly Property AsyncState() As [Object] _
                Implements IAsyncResult.AsyncState
            Get
                Return _state
            End Get
        End Property
    
        ReadOnly Property CompletedSynchronously() As Boolean _
                Implements IAsyncResult.CompletedSynchronously
            Get
                Return False
            End Get
        End Property
    
        Public Sub New(ByVal callback As AsyncCallback, _
                ByVal context As HttpContext, _
                ByVal state As [Object])
            _callback = callback
            _context = context
            _state = state
            _completed = False
        End Sub
    
        Public Sub StartAsyncWork()
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf StartAsyncTask), Nothing)
    
        End Sub
    
        Private Sub StartAsyncTask(ByVal workItemState As [Object])
            _context.Response.Write("<p>Completion IsThreadPoolThread is " & Thread.CurrentThread.IsThreadPoolThread & "</p>" & vbCrLf)
    
            _context.Response.Write("Hello World from Async Handler!")
            _completed = True
            _callback(Me)
    
        End Sub 'StartAsyncTask
    End Class 'AsynchOperation
    
    using System;
    using System.Web;
    using System.Threading;
    
    class HelloWorldAsyncHandler : IHttpAsyncHandler
    {
        public bool IsReusable { get { return false; } }
    
        public HelloWorldAsyncHandler()
        {
        }
        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
        {
            context.Response.Write("<p>Begin IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n");
            AsynchOperation asynch = new AsynchOperation(cb, context, extraData);
            asynch.StartAsyncWork();
            return asynch;
        }
    
        public void EndProcessRequest(IAsyncResult result)
        {
        }
    
        public void ProcessRequest(HttpContext context)
        {
            throw new InvalidOperationException();
        }
    }
    
    class AsynchOperation : IAsyncResult
    {
        private bool _completed;
        private Object _state;
        private AsyncCallback _callback;
        private HttpContext _context;
    
        bool IAsyncResult.IsCompleted { get { return _completed; } }
        WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } }
        Object IAsyncResult.AsyncState { get { return _state; } }
        bool IAsyncResult.CompletedSynchronously { get { return false; } }
    
        public AsynchOperation(AsyncCallback callback, HttpContext context, Object state)
        {
            _callback = callback;
            _context = context;
            _state = state;
            _completed = false;
        }
    
        public void StartAsyncWork()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(StartAsyncTask), null);
        }
    
        private void StartAsyncTask(Object workItemState)
        {
    
            _context.Response.Write("<p>Completion IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n");
    
            _context.Response.Write("Hello World from Async Handler!");
            _completed = true;
            _callback(this);
        }
    }
    

    The code implements the BeginProcessRequest method. The method writes a string to the Response property of the current HttpContext object, creates a new instance of the AsyncOperation class, and calls the StartAsyncWork method. The StartAsyncWork method then adds the StartAsyncTask delegate to the ThreadPool object. When a thread becomes available, the StartAsyncTask method is called, which writes out another string to the Response property, and then the task finishes by invoking the AsyncCallback delegate.

Registering the Custom HTTP Handler

Once you have created the custom HTTP handler class, you must register it in the Web.config file so that ASP.NET will service requests made to files with the .SampleAsync extension.

To register the custom HTTP handler in the Web.config file

  1. If your Web site does not already have a Web.config file, create one.

  2. Add the following code to your Web.config file:

    <configuration>
      <system.web>
        <httpHandlers>
          <add verb="*" path="*.SampleAsync" 
            type="HelloWorldAsyncHandler"/>
        </httpHandlers>
      </system.web>
    </configuration>
    

    The code registers the HelloWorldAsyncHandler handler as the handler for requests that end with .SampleAsync.

Configuring IIS for the HTTP Handler Extension

IIS pass requests only for certain file types to ASP.NET to service. By default, files with extensions such as .aspx, .ascx, .asmx, are already mapped to the ASP.NET. However, if you want file name extensions that you have defined to be handled by ASP.NET, you must register them in IIS. For more information, see ASP.NET Application Life Cycle Overview.

To map the extension in IIS

  1. Open Internet Services Manager.

  2. Right-click your application and choose Properties.

  3. In the Directory tab, click Configuration.

  4. Select the Mappings tab.

  5. Add a new association that maps .SampleAsync to the version of the Aspnet_isapi.dll you wish to use.

  6. If you want your handler to run regardless of whether a file exists with the name that the user requests, clear the Check if the file exists check box.

Testing the Custom HTTP Handler

Once you have created and registered your custom HTTP handler, you can test it by requesting a resource with the .SampleAsync extension from the application.

To test your custom HTTP handler

  • Browse to your application and enter a URL in the browser that ends in .SampleAsync.

See Also

Tasks

How to: Create Synchronous HTTP Handlers

Concepts

Introduction to HTTP Handlers
ASP.NET Application Life Cycle Overview