Comment : créer un gestionnaire HTTP asynchrone

Mise à jour : novembre 2007

Cette procédure pas à pas illustre la création d'un gestionnaire HTTP asynchrone. Les gestionnaires HTTP asynchrones vous permettent de démarrer un processus externe (tel qu'un appel de méthode à un serveur distant), tout en poursuivant leur traitement. Le gestionnaire peut poursuivre ses tâches sans avoir à attendre la fin du processus externe.

Pendant le traitement d'un gestionnaire HTTP asynchrone, ASP.NET replace le thread qui serait en principe utilisé pour le processus externe dans le pool de threads, jusqu'à ce que le gestionnaire reçoive un rappel du processus externe. Cela permet d'éviter le blocage du thread et d'améliorer les performances, car seul un nombre limité de threads peut être exécuté simultanément. Si plusieurs utilisateurs demandent des gestionnaires HTTP synchrones qui dépendent de processus externes, le système d'exploitation peut rapidement manquer de threads, beaucoup d'entre eux étant bloqués, en attente d'un processus externe.

L'exemple utilisé dans cette procédure pas à pas montre un gestionnaire HTTP asynchrone qui traite des demandes pour les fichiers avec l'extension de nom de fichier .SampleAsync dans une application ASP.NET. L'exemple illustre le code correspondant au gestionnaire et montre comment mapper l'extension .SampleAsync au gestionnaire dans ASP.NET. Il montre également comment mapper l'extension .SampleAsync à ASP.NET dans IIS, afin que ce dernier transmette les demandes se terminant par .SampleAsync à ASP.NET.

Pour plus d'informations sur la manière dont l'exécution ASP.NET interagit avec IIS 6.0, consultez Vue d'ensemble du cycle de vie des applications ASP.NET pour IIS 5.0 et 6.0. Pour plus d'informations sur l'intégration ASP.NET avec IIS 7.0, consultez Vue d'ensemble du cycle de vie des applications ASP.NET pour IIS 7.0.

Cette procédure pas à pas décrit notamment les tâches suivantes :

  • Création du code pour une classe de gestionnaire HTTP. La classe doit implémenter la méthode ProcessRequest et la propriété IsReusable.

  • Inscription du gestionnaire dans le fichier Web.config et mappage de l'extension de nom de fichier .SampleAsync à celui-ci.

  • Mappage de l'extension de nom de fichier .sample à ASP.NET dans IIS.

Composants requis

Pour exécuter cette procédure pas à pas, vous devez disposer des composants suivants :

  • Visual Studio ou Visual Web Developer ;

  • Un site Web ASP.NET que vous pouvez exécuter à l'aide des services IIS ;

  • IIS 6.0 ou IIS 7.0.

Création d'une classe de gestionnaire HTTP asynchrone

Pour commencer, vous allez créer une classe qui implémente le gestionnaire asynchrone.

Pour créer la classe de gestionnaire HTTP HelloWorldAsyncHandler

  1. Si le site Web avec lequel vous travaillez ne comporte pas de dossier App_Code, créez-en un sous la racine du site.

  2. Dans le répertoire App_Code, créez une classe nommée HelloWorldAsyncHandler et ajoutez le code suivant au fichier de classe :

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

    Le code implémente la méthode BeginProcessRequest. La méthode écrit une chaîne dans la propriété Response de l'objet HttpContext actuel, crée une nouvelle instance de la classe AsyncOperation et appelle la méthode StartAsyncWork. La méthode StartAsyncWork ajoute ensuite le délégué StartAsyncTask à l'objet ThreadPool. Lorsqu'un thread devient disponible, la méthode StartAsyncTask est appelée et écrit une autre chaîne dans la propriété Response. La tâche se termine alors par un appel du délégué AsyncCallback.

Inscription du gestionnaire HTTP personnalisé dans IIS 6.0

Après avoir créé la classe de gestionnaire HTTP personnalisée, vous devez l'enregistrer dans le fichier Web.config de l'application. Cela permet à ASP.NET de rechercher le gestionnaire lorsque des demandes de ressources dont l'URL se termine par .SampleAsync sont effectuées.

Différentes procédures permettent d'inscrire un gestionnaire, selon la version d'IIS utilisée (IIS 6.0 ou IIS 7.0). Cette section décrit comment inscrire un gestionnaire dans IIS 6.0. La section suivante décrit comment inscrire un gestionnaire dans IIS 7.0.

Pour inscrire le gestionnaire dans IIS 6.0

  1. Si le site Web ne possède pas de fichier Web.config, créez-en un à la racine du site.

  2. Ajoutez au fichier Web.config le balisage en surbrillance suivant :

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

    L'élément de configuration inscrit le gestionnaire HelloWorldAsyncHandler comme gestionnaire des demandes se terminant par .SampleAsync.

  3. Enregistrez un mappage d'extension de fichier pour l'extension de nom de fichier .SampleAsync à l'aide d'Gestionnaire des services IIS. Pour plus d'informations, consultez Comment : configurer une extension du gestionnaire HTTP dans IIS.

Inscription du gestionnaire HTTP personnalisé dans IIS 7.0

Dans IIS 7.0, une application peut s'exécuter en mode classique ou intégré. En mode classique, le traitement des demandes est fortement similaire à celui appliqué dans IIS 6.0. En mode intégré, IIS 7.0 gère les demandes à l'aide d'un pipeline qui lui permet de partager demandes, modules et autres fonctionnalités avec ASP.NET.

Pour IIS 7.0, l'inscription de gestionnaire requiert d'inscrire le gestionnaire soit dans le fichier Web.config, soit dans Gestionnaire des services IIS. L'administration étant centralisée dans IIS 7.0, toute modification apportée dans le fichier Web.config d'une application est répercutée dans l'interface Gestionnaire des services IIS pour l'application et inversement. Dans les procédures qui suivent, les gestionnaires sont inscrits dans le fichier Web.config.

Différentes procédures permettent d'inscrire le gestionnaire dans IIS 7.0 en mode classique et en mode intégré. Suivez la procédure correspondant au mode IIS que vous utilisez.

Pour inscrire le gestionnaire dans IIS 7.0 en mode classique

  1. Si le site Web ne possède pas de fichier Web.config, créez-en un à la racine du site.

  2. Ajoutez l'élément en surbrillance suivant à votre fichier Web.config.

    Remarque :

    Substituez le chemin d'accès correct au fichier aspnet_isapi.dll. Le fichier .dll se trouve dans le dossier où le .NET Framework est installé. Par défaut, il s'agit du chemin suivant : C:\WINDOWS\Microsoft.NET\Framework\version.

    <configuration>
      <system.web>
        <httpHandlers>      <add verb="*" path="*.SampleAsync"         type="HelloWorldAsyncHandler"/>    </httpHandlers>
      </system.web>
      <system.webServer>
        <handlers>      <add  verb="*" path="*.SampleAsync"        name="HelloWorldAsyncHandler"        type="HelloWorldAsyncHandler"        modules="IsapiModule"/>        scriptProcessor="%path%\aspnet_isapi.dll"    </handlers>
      </system.webServer>
    </configuration>
    

    L'élément de configuration inscrit le gestionnaire personnalisé par nom de classe et mappe l'extension de nom de fichier .SampleAsync à ce gestionnaire.

    Remarque :

    Parce que vous inscrivez une extension de nom de fichier personnalisée, l'inscription du gestionnaire se fait à la fois dans la section handlers et dans la section httpHandlers. En mode classique, pour la compatibilité descendante, le gestionnaire est spécifié comme un module ISAPI à l'aide de l'attribut modules. Le chemin d'accès du dll ISAPI ASP.NET est spécifié à l'aide de l'attribut scriptProcessor. L'attribut name est requis dans la section handlers.

Pour inscrire le gestionnaire dans IIS 7.0 en mode intégré

  1. Si le site Web ne possède pas de fichier Web.config, créez-en un à la racine du site.

  2. Ajoutez l'élément en surbrillance suivant à votre fichier Web.config.

    <configuration>
      <system.webServer>
        <handlers>      <add verb="*" path="*.SampleAsync"        name="HelloWorldAsyncHandler"        type="HelloWorldAsyncHandler"/>    </handlers>
      </system.webServer>
    </configuration>
    

    L'élément de configuration inscrit le gestionnaire personnalisé par nom de classe et mappe l'extension de nom de fichier .SampleAsync à ce gestionnaire.

    Remarque :

    L'inscription se fait dans la section handlers, mais pas dans la section httpHandlers. L'attribut name est requis.

Test du gestionnaire HTTP personnalisé

Après avoir créé et inscrit le gestionnaire HTTP personnalisé, vous pouvez le tester.

Pour tester votre gestionnaire HTTP personnalisé

  • Naviguez jusqu'à l'application et entrez dans le navigateur une URL qui se termine par .SampleAsync.

    Le texte défini dans la classe HelloWorldAsyncHandler est affiché.

Voir aussi

Tâches

Procédure pas à pas : création d'un gestionnaire HTTP synchrone

Concepts

Vue d'ensemble du cycle de vie des applications ASP.NET pour IIS 5.0 et 6.0

Autres ressources

Introduction aux gestionnaires HTTP