Cómo: Crear un controlador HTTP asincrónico

Actualización: noviembre 2007

Los controladores HTTP asincrónicos permiten iniciar un proceso externo, como por ejemplo una llamada a un método en un servidor remoto, mientras que el controlador continúa procesando sin esperar a que termine el proceso externo. Durante el procesamiento de un controlador HTTP asincrónico, ASP.NET coloca el subproceso que normalmente se utilizaría para el proceso externo de nuevo en el grupo de subprocesos hasta que el controlador reciba una devolución de llamada del proceso externo. Esto puede evitar el agrupamiento en bloques de los subprocesos y mejorar el rendimiento ya que solo es posible ejecutar inmediatamente un número limitado de subprocesos. Si varios usuarios solicitan controladores HTTP sincrónicos que dependan de procesos externos, el sistema operativo puede quedarse sin subprocesos rápidamente porque muchos estarán bloqueados y esperando un proceso externo.

El ejemplo de código siguiente muestra un controlador HTTP asincrónico que procesa solicitudes para los archivos con la extensión de nombre de archivo .SampleAsync dentro de una aplicación ASP.NET. El ejemplo muestra el código para el controlador y después cómo asignar la extensión .SampleAsync al controlador en ASP.NET. Finalmente, el ejemplo muestra cómo asignar la extensión .SampleAsync a ASP.NET en IIS, de modo que IIS reenvíe las solicitudes que terminen en .SampleAsync a ASP.NET.

Para obtener más información sobre cómo interactúa el motor en tiempo de ejecución de ASP.NET con IIS, vea Información general sobre el ciclo de vida de una aplicación ASP.NET para IIS 5.0 y 6.0.

Para crear la clase de controlador HTTP HelloWorldAsyncHandler

  • Cree una clase denominada HelloWorldAsyncHandler en su directorio App_Code y agregue el código siguiente al archivo de clase:

    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);
        }
    }
    

    El código implementa el método BeginProcessRequest. El método escribe una cadena en la propiedad Response del objeto actual HttpContext, crea una nueva instancia de la clase AsyncOperation y llama al método StartAsyncWork. El método StartAsyncWork agrega a continuación el delegado StartAsyncTask al objeto ThreadPool. Cuando un subproceso esté disponible, se llama al método StartAsyncTask, que escribe otra cadena en la propiedad Response y después la tarea finaliza al invocar al delegado AsyncCallback.

Registrar el controlador HTTP personalizado

Una vez creada la clase del controlador HTTP personalizado, se debe registrar en el archivo Web.config para que ASP.NET atienda las solicitudes de los archivos con la extensión .SampleAsync.

Para registrar el controlador HTTP personalizado en el archivo Web.config

  1. Si su sitio Web aún no tiene un archivo Web.config, cree uno.

  2. Agregue el código siguiente al archivo Web.config:

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

    El código registra el controlador HelloWorldAsyncHandler como el controlador para solicitudes que finalizan con .SampleAsync.

Configurar IIS para la extensión de controlador HTTP

IIS sólo pasa las solicitudes para ciertos tipos de archivo a ASP.NET para darles servicio. De forma predeterminada, los archivos con extensiones como .aspx, .ascx y .asmx ya están asignados a ASP.NET. Sin embargo, si desea que ASP.NET controle las extensiones de nombre de archivo definidas, debe registrarlas en IIS. Para obtener más información, vea Información general sobre el ciclo de vida de una aplicación ASP.NET para IIS 5.0 y 6.0.

Para asignar la extensión en IIS

  1. Abra el Administrador de servicios de Internet.

  2. Haga clic con el botón secundario del mouse (ratón) en la aplicación y elija Propiedades.

  3. En la ficha Directorio, haga clic en Configuración.

  4. Seleccione la ficha Asignaciones.

  5. Agregue una nueva asociación que asigne .SampleAsync a la versión de Aspnet_isapi.dll que desea utilizar.

  6. Si desea que el controlador se ejecute sin tener en cuenta la existencia o no de un archivo con el nombre que solicita el usuario, borre la casilla de verificación Comprobar si el archivo existe.

Probar el controlador HTTP personalizado

Una vez creado y registrado, puede probar el controlador HTTP personalizado solicitando un recurso con la extensión .SampleAsync de la aplicación.

Para probar el controlador HTTP personalizado

  • Vaya a su aplicación y escriba una dirección URL en el explorador que finaliza en .SampleAsync.

Vea también

Tareas

Tutorial: Crear un controlador HTTP sincrónico

Conceptos

Información general sobre el ciclo de vida de una aplicación ASP.NET para IIS 5.0 y 6.0

Otros recursos

Introducción a los controladores HTTP