Non mi fermo mai ed anche ora che sono in ferie continuo a …. programmare !
Questa volta tocca alla mia uPLibrary che ho aggiornato alla versione 2.0 (ovviamente anche su Nuget con il supporto a ben tre versioni del .Net Micro Framework, dalla 4.1 all’attuale 4.3) con un nuovo componente : un client HTTP.
Tutti vi starete facendo delle domande sul perché della necessità di riscrivere un client HTTP considerando che esistono le già ampiamente collaudate classi HttpWebRequest edHttpWebResponse direttamente incluse nel .Net Micro Framework. Ebbene, in alcune board con poca memoria (vedi Netduino), gli assembly relativi a queste due classi occupano troppo spazio e lavorando con degli stream sono poco performanti. Per questo motivo, ho deciso di realizzare un semplice client (ogni consiglio per il suo miglioramento è ben accetto) che sfrutta direttamente le socket senza alcuno strato intermedio di astrazione (concetto che molte volte nei sistemi embedded bisogna limitare per migliorare le performance).
Il modello di programmazione è abbastanza semplice, in quanto prevede l’uso della classeHttpClient che mette a disposizione i tre seguenti metodi principali :
-
Send() : per eseguire una generica richiesta HTTP (GET, POST, PUT, …);
-
Get() : metodo ad hoc per eseguire richieste di tipo GET;
-
Post() : metodo ad hoc per eseguire richieste di tipo POST;
Il metodo Send() riceve in ingresso un oggetto di tipo HttpRequest nel quale vanno specificati tutti i dati che compongono la richiesta (metodo HTTP, URI di destinazione ed eventuali headers). Esso ritorna un oggetto di tipo HttpResponse con tutte le informazioni sulla risposta ricevuta dal server. Per poter gestire la ricezione di un body (nel caso di GET) o l’invio (nel caso di POST o PUT), è possibile utilizzare i seguenti due eventi che la classe espone :
-
RecvBody : sollevato nel momento in cui ci sono dati nel body in ricezione e possono essere acquisiti;
-
SendBody : sollevato dal client solo nel caso in cui c’è un event handler registrato, che sancisce la volontà di voler trasferire dei dati al client attraverso il body;
In caso di ricezione, l’event handler riceve in ingresso l’oggetto HttpResponse attraverso il quale poter accedere alla proprietà Body su cui invocare il metodo Read(), che legge i dati direttamente dalla socket restituendoli in un buffer da noi specificato. L’evento viene sollevato più volte fino al completamento del body.
In caso di invio, l’event handler riceve in ingresso l’oggetto HttpRequest attraverso il quale poter accedere sempre alla proprietà Body su cui invoare il metodo Write() al quale poter passare un buffer con i dati da scrivere sulla socket sottostante. Questo evento viene sollevato una sola volta dal client, per cui è necessario effettuare al suo interno eventualmente più chiamate su Body.Write() fino alla completa trasmissione del body desiderato.
Di seguito è riportato un banalissimo esempio per ricevere la pagina del sito Microsoft.
public static void Main() { HttpRequest req = new HttpRequest(); req.Method = HttpMethod.Get; req.Uri = new Uri("http://www.microsoft.com"); req.Headers.Add("Accept", "text/html"); HttpClient client = new HttpClient(); client.RecvBody += client_RecvBody; client.Send(req); } static void client_RecvBody(HttpResponse httpResp) { byte[] buffer = new byte[1024]; int read = httpResp.Body.Read(buffer, 0, 1024); Debug.Print(new String(Encoding.UTF8.GetChars(buffer))); }
Riprendendo l’esempio precedente, attraverso il metodo Get() avremmo il seguente codice.
public static void Main() { HttpClient client = new HttpClient(); client.Get("http://www.microsoft.com", client_RecvBody); }
Nel caso di collegamento ad un servizio REST al quale dover trasmettere dei dati, possiamo pensare ad un utilizzo di questo tipo.
public static void Main() { HttpClient client = new HttpClient(); client.Post("http://myserver/api/", client_SendBody); } static void client_SendBody(HttpRequest httpReq) { byte[] buffer = Encoding.UTF8.GetBytes(""); httpReq.ContentLength = buffer.Length; httpReq.Body.Write(buffer, 0, httpReq.ContentLength); }