Nouvelles Chroniques d'Amethyste

Penser au Sens, pas au Verbe

Emettre une requête Http en C#

Poster un commentaire

On utilise de plus en plus souvent des interfaces web api dans les applications, il est donc important de connaître quelques techniques pour émettre des requêtes Http.

.Net propose nativement 3 classes pour faire le boulot:

  1. System.Net.HttpWebRequest
  2. System.Net.Http.HttpClient
  3. System.Net.WebClient

Je ne vais pas aborder HttpWebRequest qui est marquée comme obsolète.

Et puis il y a aussi les frameworks, le plus connu étant RestSharp que je vais aussi présenter.

Allons y!

Projet de test

Le projet est composé d’une librairie qui définie la classe Livre, d’un client Console qui attaque un web service dont voici l’unique contrôleur:

public class IndexController : ApiController
{
   // GET: api/Index
   public IEnumerable<Livre> Get()
   {
      return new Livre[] {
         new Livre() {Titre="Les Hauts de Hurlevent", Auteur="Emilie Brontoë" },
         new Livre() {Titre="La gloire de mon père", Auteur="Marcel pagnol" } };
   }
 
   // POST: api/Index
   public void Post([FromBody]Livre value)
   {
 
   }
}

 

Livre maintenant:

 


public class Livre
{
   public string Titre { get; set; }
 
   public string Auteur { get; set; }
}

 

Pour la console, on verra dans les rubriques qui suivent.

HttpClient

HttpClient nécessite .Net 4.5 et n’expose que des méthodes asynchrones. On peut installer la librairie avec Nuget.

Ecrivons le client, c’est à dire Main:

var task = Task.Run<string>(() =>
{
   return DownloadLibraryAsync();
});
 
Console.WriteLine(task.Result);
Console.ReadLine();

 

Et la méthode principale, asynchrone bien entendu:

static async Task<string> DownloadLibraryAsync()
{
   // notre cible
   string page = "http://localhost:49943/api/index";
 
   using (HttpClient client = new HttpClient())
   {
      // autre possibilité
      //client.BaseAddress = new Uri(page);
 
      // on peut compléter le header
      //client.DefaultRequestHeaders.Add("X-TEST", "123");
 
      // la requête
      using (HttpResponseMessage response = await client.GetAsync(page))
      {
 
          using (HttpContent content = response.Content)
         {
            // récupère la réponse, il ne resterai plus qu'à désérialiser
            string result = await content.ReadAsStringAsync();
            return result;
         }
      }
   }
}

La première étape consiste à instancier un HttpClient qui est disposable comme tout le reste d’ailleurs. C’est donc bien de ne pas oublier les appels à Dispose.

Au passage remarquez les commentaires qui présentent des alternatives possibles.

On fait la requête avec GetAsync. Comme l’indique son nom, il s’agit d’une méthode nativement asynchrone. On va donc l’appeler de façon asynchrone avec await puisque l’on est en .Net 4.5. On récupère la réponse d’où on extrait… la réponse du service!

Cette réponse est récupérée sous la forme d’une String. Dans un monde parfait il faudrait ensuite désérialiser. La sortie ressemble à ceci:

2015-11-25_23-13-09

 

GET est rarement le plus compliqué dans les frameworks, testons donc un POST.

Le défi sera d’envoyer une instance de Livre dans le body et de le réceptionner côté service. C’est partit!

 

On aura besoin d’ajouter une référence à System.Net.Http.Formatting qui complète HttpClient avec diverses méthodes d’extension bien utiles. Nous allons explorer plusieurs méthodes.

 

Il peut arriver que nous ayons une String à transférer ou bien qu’il soit facile de sérialiser l’objet à transférer:

static async Task PostBookAsync()
{
   // notre cible
   string page = "http://localhost:49943/api/index";
 
   using (HttpClient client = new HttpClient())
   {
      HttpContent contentPost = new StringContent("{Titre:\"Tartuffe\", Auteur:\"Molière\"}",
      Encoding.UTF8, "application/json");
 
      var response = await client.PostAsync(new Uri(page), contentPost)
            .ContinueWith(
               (t) => t.Result.EnsureSuccessStatusCode()
             );
   }
}

Le passage de la String s’effectue à l’aide de StringContent qui est une instance de la classe abstraite HttpContext.

Cette classe permet de modéliser un body et un content, on la passe en paramètre de PostAsync qui émet un POST asynchrone.

Je vous laisse vérifier que côté service on reçoit une instance de Livre.

 

On peut aussi transférer une classe complexe comme une instance de Livre.

static async Task PostBookAsync()
{
   Livre livre = new Livre() { Titre = "Tartuffe", Auteur = "Molière" };
 
   // notre cible
   string page = "http://localhost:49943/api/index";
 
   using (HttpClient client = new HttpClient())
   {
      var response = await client.PostAsJsonAsync(page,livre)
           .ContinueWith(
              (t) => t.Result.EnsureSuccessStatusCode()
           );
   }
}

Le transfert est effectué en invoquant PostAsJsonAsync, on peut aussi émettre du XML.

 

Pour finir on peut traiter le cas où on a une série de valeurs à émettre plutôt qu’une simple instance d’un objet. Le code qui suit traite ce cas.

string page = "http://localhost:49943/api/index";
 
using (HttpClient client = new HttpClient())
{
   var postData = new List<KeyValuePair<string, string>>();
   postData.Add(new KeyValuePair<string, string>("Titre", "Tartuffe"));
   postData.Add(new KeyValuePair<string, string>("Auteur ", "Molière"));
 
   HttpContent content = new FormUrlEncodedContent(postData);
 
   var response = await client.PostAsJsonAsync(page, content)
      .ContinueWith(
         (t) => t.Result.EnsureSuccessStatusCode()
      );
}

On a quoi la dedans? Une liste de KeyValuePair, c’est une façon de construire un objet qui ressemble à un dictionnaire, mais dans une collection.

FormUrlEncodedContent nous permet de transformer notre collection en un HttpContent qui sera passé en paramètre de PostAsJsonAsync.

 

Côté service on pourrait créer une méthode d’action similaire à celle-ci:

public void Post([FromBody]List<KeyValuePair<string, string>> message)
{
 
}

Mais on peut aussi récupérer le résultat directement dans la réponse.


public void Post()
{
   var httpContent = Request.Content;
   var asyncContent = httpContent.ReadAsStringAsync().Result;
}

 

Il ne reste plus qu’à désérialiser.

WebClient

Nous allons reprendre les exemples qui précèdent et en particulier le même service.

Le code est le suivant:

public static async Task<string> DownloadLibrary()
{
   var page = new Uri("http://localhost:49943/api/index");
 
   using (WebClient client = new WebClient())
   {
      client.Encoding = Encoding.UTF8;
      return await client.DownloadStringTaskAsync(page);
   }
 
}

On pourrait le lancer ainsi:


Task<string> task = DownloadLibrary();
Console.WriteLine(task.Result);

La classe propose d’autres versions asynchrones. WebClient est une ancienne API qui a suivit toutes les évolutions de .Net en matière d’asynchronisme et gère en particulier les patterns EAP et APM décrits ici:

https://amethyste16.wordpress.com/2014/10/03/les-nouveaux-chemins-vers-lasynchronisme-ii/

 

Et que se passe t’il pour des appels POST?

var page = new Uri("http://localhost:49943/api/index");
 
using (WebClient client = new WebClient())
{
   return await client.UploadStringTaskAsync(page, "{Titre:\"Tartuffe\", Auteur:\"Molière\"}");
}

 

Dans notre exemple la chaîne de retour est vide, mais on aurait pu envoyer le nom d’un fichier et recevoir son contenu.

Il existe d’autres variantes pour manipuler du binaire par exemple.

 

 

Si vous vous intéressez à une comparaison entre WebClient et HttpClient:

http://www.diogonunes.com/blog/webclient-vs-httpclient-vs-httpwebrequest/

RestSharp

RestSharp existe à partir de .Net 3.5. C’est également une classe solution portable et multi-plateforme. RestSharp est à la fois simple d’emploi et très puissant, c’est à mon avis le meilleur choix dans une application.

 

Regardons tout de suite comment on l’utilise:

static void Main(string[] args)
{
   Task<IRestResponse<string>> task = DownLoadAsync();
   var response = task.Result;
 
   Console.WriteLine(response.Content);
   Console.ReadLine();
}
 
public static async Task<IRestResponse<string>> DownLoadAsync()
{
   var page = "http://localhost:49943/api";
 
   // on passe l'url de base
   var client = new RestClient(page);
 
   var requete = new RestRequest("index");
   return await client.ExecuteGetTaskAsync<string>(requete);
}

 

ExecuteGetTaskAsync émet une requête GET comme le suggère son nom. Il existe une version pour les principaux verbes.

RestSharp propose des méthodes qui effectuent la désérialisation.

 

Note: Depuis la version 103.0, JSON.Net n’est plus le désérialiseur par défaut et ne fait plus partie des dépendances.

La raison provient d’un problème de versionnage dans une nouvelle version de JSON.Net apparemment.

https://groups.google.com/forum/#!msg/restsharp/yjGCtOnEYHk/XbKZi0yjN8cJ

 

Restsharp embarque par défaut tout l’outillage pour gérer la désérialisation, mais il est possible d’utiliser un désérialiseur personnalisé.

 

public static async Task<IRestResponse<List<Livre>>> DownLoadAsync()
{
   var page = "http://localhost:49943/api";
 
   // on passe l'url de base
   var client = new RestClient(page);
 
   var requete = new RestRequest("index");
   return await client.ExecuteGetTaskAsync<List<Livre>>(requete);
}

 

On peut écrire ce code autrement:

static void Main(string[] args)
{
   var task = GetLibraryAsync();
   List<Livre> livres = task.Result;
 
   Console.ReadLine();
}
 
private static async Task<IRestResponse<T>> DownLoadAsync<T>()
{
   var page = "http://localhost:49943/api";
 
   // on passe l'url de base
   var client = new RestClient(page);
 
   var requete = new RestRequest("index");
return await client.ExecuteGetTaskAsync<T>(requete);
}
 
public static async Task<List<Livre>> GetLibraryAsync()
{
   var livres =  await DownLoadAsync<List<Livre>>().ContinueWith
   (
      (t) =>
      {
         var reponse = t.Result;
         return reponse.Data;
      }
   );
 
   return livres;
}

 

Il est également très simple de poster une valeur. Il y a plusieurs façon de faire ça, en voici une:

var page = "http://localhost:49943/api";
Livre livre = new Livre() { Titre = "Tartuffe", Auteur = "Molière" };
 
// on passe l'url de base
var client = new RestClient(page);
 
var requete = new RestRequest("index", Method.POST);
requete.AddJsonBody(livre);
return await client.ExecutePostTaskAsync(requete);

 

Cette fois je me sers d’une méthode plus générique, ExecutePostTaskAsync, il nous appartient alors de passer le nom du verbe HTTP dans la requête.

Un autre tutoriel si vous souhaitez en savoir plus:

https://visualstudiomagazine.com/articles/2015/10/01/consume-a-webapi.aspx

 

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s