Avant-propos▲
En ASP.NET, il existe plusieurs manières d'interroger une ressource web afin de récupérer sa source (HTML, XML…). La manière la plus simple et la plus fréquemment utilisée consiste à faire appel à la méthode WebRequest.GetResponse. Néanmoins cette dernière possède un inconvénient majeur dans la mesure où cette opération est synchrone et génère un temps de latence assez important et ce même pour récupérer un flux web de petite taille.
I. Utilisation de la méthode BeginGetResponse▲
La méthode BeginGetResponse permet d'interroger un flux de manière asynchrone pour ce faire, il suffit de lui indiquer en tant que premier argument l'adresse d'une méthode callback. Le deuxième argument à spécifier est un objet appelle couramment state (état). Cet objet état permet d'établir une connexion logique entre le thread principal effectuant requête initiale et la méthode de callback appelée à s'exécuter au sein d'un thread différent.
Il est nécessaire par ailleurs de définir et d'instancier sa propre classe d'état à passer en argument. Cette classe peut contenir des références aux objets que vous souhaitez transmettre a votre callback notamment une référence sur l'objet HttpWebRequest comme illustre dans le code ci-dessous :
' La classe RequestState est utilise pour transmettre l'objet HttpWebRequest
' a travers l'appel asynchrone
Public
Class
RequestState
Public
Request
As
HttpWebRequest
Public
Sub
New
(
)
Request
=
Nothing
End
Sub
End
Class
' Classe effectuant un appel HTTP GET asynchrone ver une URL
Public
Class
GetHttpAsync
Public
Sub
GetPage
(
ByVal
url As
String
)
Dim
req As
HttpWebRequest
req =
CType
(
WebRequest.Create
(
url), HttpWebRequest)
' Creation de l'objet etat
Dim
rs As
New
RequestState
' On ajoute la requete dans l'objet etat pour pouvoir le recuperer dans la callback
rs.Request
=
req
' Appel asynchrone
req.BeginGetResponse
(
New
AsyncCallback
(
AddressOf
ResponseCallback), rs)
End
Sub
Par la suite, dans la callback, il suffit de faire appel à la propriété AsyncRequest pour récupérer l'objet RequestState (état), et ainsi l'objet HttpWebRequest. Enfin, l'appel a la méthode EndGetResponse permet de récupérer l'objet HttpWebResponse. Il est ensuite possible de procéder comme si vous aviez fait un appel synchrone.
Private
Shared
Sub
ResponseCallback
(
ByVal
ar As
IAsyncResult)
' Recuperation l'objet etat
Dim
rs As
RequestState =
CType
(
ar.AsyncState
, RequestState)
' Recuperation de la requete web (object HttpWebRequest)
Dim
req As
HttpWebRequest =
rs.Request
' Recuperation de la reponse web
Dim
resp As
HttpWebResponse =
CType
(
req.EndGetResponse
(
ar), HttpWebResponse)
Dim
responseStream As
Stream =
resp.GetResponseStream
(
)
Dim
sr As
StreamReader =
New
StreamReader
(
responseStream, Encoding.UTF8
)
Dim
strContent As
String
=
sr.ReadToEnd
(
)
responseStream.Close
(
)
End
Sub
II. Gestion d'un timeout▲
Pour terminer, je souhaiterais répondre à une question qui revient souvent dans les forums dotnet que j'ai eu l'occasion de fréquenter à savoir comment annuler une requête HTTP s'exécutant de manière asynchrone. Ce qui peut être vivement souhaité si l'opération prend trop de temps (si le serveur web que l'on souhaite interroger est surchargé par exemple).
Il est possible d'annuler une requête en appelant la méthode Abort sur l'objet HttpWebRequest. Il existe plusieurs manières d'enregistrer un timeout sur une requête HTTP et d'annuler cette dernière une fois que le timeout est dépassé. La méthode la plus simple consiste à utiliser le thread pool du framework .NET en utilisant la méthode WaitOrTimerCallback et d'y associer une méthode déléguée comme le montre le code ci-dessous :
' Classe effectuant un appel HTTP GET asynchrone avec timeout
Public
Sub
DoAsyncWithTimeout
(
ByVal
url As
String
)
Dim
rs As
New
RequestState
Dim
req As
HttpWebRequest
req =
CType
(
WebRequest.Create
(
url), HttpWebRequest)
'Appel asynchrone
Dim
ar As
IAsyncResult =
rs.Request.BeginGetResponse
(
New
AsyncCallback
(
AddressOf
ResponseCallback), rs)
'Définition d'un timeout de 10 secondes
ThreadPool.RegisterWaitForSingleObject
(
ar.AsyncWaitHandle
, _
New
WaitOrTimerCallback
(
AddressOf
RequestTimeoutCallback), _
rs, 10
*
1000
, _
True
_
)
End
Sub
Public
Shared
Sub
RequestTimeoutCallback
(
ByVal
state As
Object
, ByVal
timedout As
Boolean
)
If
timedout Then
Dim
rs As
RequestState =
CType
(
state, RequestState)
If
Not
rs Is
Nothing
And
Not
rs.Request
Is
Nothing
Then
rs.Request.Abort
(
)
End
If
End
If
End
Sub
Dans le code précédant, si la callback enregistrée lors de l'appel BeginGetResponse ne s'exécute pas dans les 10 secondes, la méthode déléguée RequestTimeoutCallback sera invoquée. Notez bien que l'objet état associé à la requête asynchrone est aussi passé en tant qu'argument au délégué. En effet, dans cette méthode, nous avons besoin de l'objet HttpWebRequest pour annuler la requête http en appelant la méthode Abort.