Nicolò Carandini HTTP, Web Services e RestSharp (II parte) 1 HTTP, Web Services e RestSharp Dopo aver descritto nella prima parte di quest articolo 1 le basi su cui poggia la comunicazione nel Word Wide Web e illustrato le caratteristiche essenziali del protocollo HTTP, possiamo ora passare alla seconda parte, incentrata sui Web Services e l uso della libreria RestSharp. Diversi tipi di risorse per i diversi soggetti in gioco Come abbiamo visto nella prima parte, Il Word Wide Web è l insieme di risorse (documenti, immagini e informazioni in genere) tra loro logicamente collegate tramite riferimenti (hyperlink) e univocamente identificate (URI 2 Uniform Resource Identifier). Per accedere a tali risorse, il Client utilizza il protocollo HTTP per comunicare con il Server. Il formato in cui tali risorse vengono restituite dipende dalla natura della risorsa stessa (immagine, elenco di dati, ecc.) ma anche dall utilizzatore del Client, che può essere un essere umano o un applicativo. Nel caso in cui l utilizzatore sia un essere umano, il Client è chiamato Web Browser, il Server è una Web Application e l HTTP Response è solitamente di tipo HTML. Quando invece l utilizzatore è un applicazione, il Client è generalmente un componente dell applicazione client, il Server è un Web Service e l HTTP Response è tipicamente di tipo XML o JSON, come illustrato in figura 1: Per meglio spiegare questo concetto immaginiamo di avere un server che contiene, ad esempio, l elenco delle regioni italiane e per ciascuna di esse il numero di abitanti e la superfice in metri quadrati, e per semplicità, ipotizziamo che tali dati risiedano su un database locale del medesimo server di origine. Ipotizziamo inoltre di aver predisposto due diversi URL, ovvero due diverse risorse a cui rispondono due diverse applicazioni (una Web App e un Web Service) che pur utilizzando gli stessi dati forniscono una diversa risposta, la prima adatta all utente umano e l altra adatta all applicazione. I due diversi URL potrebbero, ad esempio, essere: http://www.mytest.com/italy/regions http://mytest.com/webapi/italy/regions E logico ipotizzare che il primo URL verrà utilizzato dall utente nel Web Browser, che riceverà come risposta una pagina HTML contenente le informazioni con una impaginazione adatta alla lettura da parte di un essere umano, probabilmente arricchita con elementi grafici e magari anche altre informazioni, ad esempio di tipo pubblicitario. 1 Pubblicato il 22/02/2012 su DomusDotNet (http://www.domusdotnet.org/articoli/http-e-restsharp-(parte-1).aspx) 2 L URI è forma più generale del ben più noto URL Uniform Resource Locator
Nicolò Carandini HTTP, Web Services e RestSharp (II parte) 2 Nel secondo caso, l URL verrà utilizzato da un applicazione che si vedrà restituire solo i dati richiesti, in formato XML o meglio ancora JSON. Ciò non di meno, nessuno vieta di utilizzare il secondo URL anche nel Web Browser, che in caso di dati XML di solito genera automaticamente una pagina HTML per la loro visualizzazione. Al contrario, l utilizzo del primo URL da parte di un applicazione rende difficile recuperare i dati desiderati. D altra parte, in assenza di un web service, alle volte capita di doverlo fare e tale operazione viene chiamata Screen Scraping. I Web Services Volendo generalizzare, possiamo vedere un Web Service come una Remote Procedure Call. La nostra applicazione, nel comunicare la richiesta di un azione da effettuare su una determinata risorsa tramite il protocollo HTTP, attiva una procedura (il Web Service) su una macchina remota. Seguendo il metodo di classificazione riportato nel libro di Leonard Richardson 3, è possibile identificare tre diverse architetture: RESTful RPC-Style REST-RPC Hybrid I Web Services di tipo RESTful 4 sono caratterizzati dall uso dei metodi HTTP per l identificazione dell azione da compiere (GET, POST, DELETE, etc. etc.) e dall uso dell URI per l identificazione della parte di risorsa su cui effettuare tale azione. I Web Services di tipo RPC-Style si caratterizzano dall uso di un proprio vocabolario di comandi, secondo una propria specifica sintassi, in modo simile ai diversi linguaggi di programmazione. Questi tipi di Web Services si riconoscono facilmente perché l azione e il contesto sono contenuti all interno della parte body del messaggio HTTP, e conseguentemente il tipo di comando HTTP non ha attinenza con l azione (ad esempio si può avere una richiesta HTTP GET che nel body contiene un azione di cancellazione di una determinata parte di risorsa!). Da notare che tutti i servizi SOAP sono di fatto servizi di tipo RPC-Style, in quanto utilizzano la parte body del messaggio HTTP per contenere il messaggio SOAP, utilizzando il comando POST di HTTP indipendentemente dall azione richiesta (contenuta nel messaggio SOAP). I web Services di tipo REST-RPC Hybrid sono I più difficili da identificare, perché pur usando l URI per identificare della parte di risorsa su cui effettuare l azione, di fatto specificano l azione come un parametro. Un classico esempio è la Flickr API, quando utilizza una richiesta HTTP di tipo GET ma include nell URI il parametro method=flickr.photos.delete che cancellando la risorsa contraddice una delle caratteristiche essenziali del metodo GET, ovvero quella di essere un metodo di sola lettura. La libreria RestSharp RestSharp è una libreria open source per.net che semplifica la creazione di applicazioni client per Web Services di tipo REST. Non è perciò adatta a ricevere dati da Web Services di tipo SOAP (per i quali occorre usare WCF). D altra parte, avendo visto nella prima parte di quest articolo che le richieste e le risposte HTTP sono formate da solo testo, perché non ce le costruiamo al volo e le utilizziamo senza usare una specifica libreria? Ovviamente è una domanda retorica, perché il protocollo HTTP pur essendo basato su messaggi di tipo testo, non è poi così banale. Scrivere un client che sappia gestire i diversi tipi di messaggi, i diversi tipi di contenuto (nel body possono esserci immagini, testo encoded in formato JSON o XML, etc. etc.), le diverse risposte che il Web Service puo restituire e moltro altro ancora, certamente non è cosa facile. 3 RESTful Web Services, di Leonard Richardson & Sam Ruby, O Reilly Editore 4 Per la definizione di architettura REST si veda http://it.wikipedia.org/wiki/representational_state_transfer
Nicolò Carandini HTTP, Web Services e RestSharp (II parte) 3 Altra domanda tipica che potrebbe sorgere è: Perché non usare direttamente WCF? La mia risposta è che se il Web Service è di tipo REST, l uso di RestSharp è molto più semplice ed immediato. Ciò non di meno, è certamente possibile utilizzare WCF (anche su Windows Phone) al posto di RestSharp. Di pari passo, è sempre possibile utilizzare System.Web.HTTPWebRequest e System.Xml.XmlReader, ma come vedremo qui di seguito, l uso di RestSharp offre a parità di semplicità d uso molte comode funzionalità aggiuntive. Passiamo dunque a descrivere le caratteristiche di RestSharp: Richiede.NET 3.5 o superiore ed è disponibile per Silverlight 4, Windows Phone 7, Mono e MonoTouch. Consente la deserializzazione automatica di risposte in formato XML e JSON Rilevamento automatico del tipo di contenuto restituito dal Web Server Nella deserializzazione utilizza una logica non banale (Fuzzy Logic) per associare l elemento XML o JSON alla corrispondente proprietà della classe (ad esempio, l elemento XML o JSON 'product_id' verrà automaticamente associato alla proprietà 'ProductId' della classe) Supporta l uso di serializzatori e deserializzatori personalizzati mediante classi che implementano le interfaccie ISerializer and IDeserializer Supporta tutti i metodi HTTP quali GET, POST, PUT, HEAD, OPTIONS e DELETE Supporta l autenticazione di tipo oauth 1, oauth 2, Basic, NTLM e Parameter-based Supporta l autenticazione personalizzata mediante classi che implementano l interfaccia IAuthenticator Consente l upload di Multi-part form/file Possiede un T4 helper per generare automaticamenye le classi C# a partire da un documento XML Per mostrare la semplicità d uso, riporto il codice relativo alla richiesta e visualizzazione dell elenco delle regioni d Italia, utilizzando una chiamata asincrona che deserializza la risposta in un istanza di una nostra classe all uopo predisposta. Come prima cosa definiamo la classe necessaria ad accogliere la deserializzazione, che ha come unica proprietà la lista di oggetti di tipo Regione: using System.Collections.Generic; namespace MySolution.WP7Client.WebApi public class RegionsOutBound // <regioni> // <regione>...</regione> //... // <regione>...</regione> // </regioni> public List<Regione> Regioni get; set; public class Regione // <regione> // <cod_istat>1234</cod_istat> // <nome>lazio</nome> // <sup_km_quad>138123</sup_km_quad> // </regione> public string CodIstat get; set; public string Nome get; set; public int SupKmQuad get; set;
Nicolò Carandini HTTP, Web Services e RestSharp (II parte) 4 Una volta definite le classi RegionsOutBound e Regione, l uso della libreria è a dir poco banale: var request = new RestRequest("/webapi/italy/regions", Method.GET); var asynchandle = client.executeasync<regionsoutbound>(request, response => foreach (Regione regione in response.data.regioni) Console.WriteLine("Regione 0: 1 km quadrati", regione.nome, regione.supkmquad); ); Il metodo generico ExecuteAsync<T> richiede come primo parametro la request e come secondo parametro una Action<RestResponse<T>> (che nel nostro esempio abbiamo implementato con un anonymus method). Il metodo ExecuteAsync<T> restituisce un handle che ci consente, se necessario, di abortire la richiesta con asynchandle.abort(). Al completamento della chiamata asincrona, RestResponse<T> contiene tutte le informazioni relative alla risposta HTTP nelle proprietà ereditate da RestResponseBase: public string Content get; set; public string ContentEncoding get; set; public long ContentLength get; set; public string ContentType get; set; public IList<RestResponseCookie> Cookies get; protected set; public Exception ErrorException get; set; public string ErrorMessage get; set; public IList<Parameter> Headers get; protected set; public byte[] RawBytes get; set; public IRestRequest Request get; set; public ResponseStatus ResponseStatus get; set; public Uri ResponseUri get; set; public string Server get; set; public HttpStatusCode StatusCode get; set; public string StatusDescription get; set; a cui aggiunge la proprietà: public T Data get; set; che rappresenta l istanza della nostra classe contenente la deserializzazione del contenuto della risposta (ovvero l HTTP body). Nel nostro esempio response.data è di tipo RegionsOutBound ed è quindi possibile utilizzare il ciclo foreach sulla sua proprietà pubblica response.data.regioni. Da notare infine che il deserializzatore ha usato la fuzzy logic per associare gli elementi XML alle proprietà della classe Regione (infatti gli elementi XML sono correttamente associati alle rispettive proprietà della classe Regione anche se gli elementi sono scritti in minuscolo e separati dall underscore mentre le proprietà della classe sono in CamelCase). Il modo in cui viene deserializzato il contenuto dipende dal content-type restituito dalla HTTP Response. In caso di application/xml o text/xml o * (ovvero di tipo non specificato) viene utilizzato il deserializzatore XML, mentre nel caso di application/json o text/json viene utilizzato il deserializzatore JSON. Volendo utilizzare un deserializzatore personalizzato, è possibile utilizzare una classe che implementa IDeserializer, ed associarlo ad un determinato content-type: // elimina la mappatura predefinita tra content-type e deserializzatori client.clearhandlers(); // crea la mappatura tra content-type text/xml e il mio deserializzatore personalizzato client.addhandler("text/xml", new mydeserializer());
Nicolò Carandini HTTP, Web Services e RestSharp (II parte) 5 L autenticazione viene gestita creando un istanza della classe di autenticazione (una per ciascun tipo di autenticazione supportata) e assegnandola alla proprietà RestClient.Authenticator: // opzionale, da usare solo se richiesto dal web service client.authenticator = new HttpBasicAuthenticator(username, password); I diversi metodi di creazione della richiesta consentono di specificare sia la risorsa che il metodo HTTP all atto della creazione oppure successivamente, assegnando i valori desiderati alle rispettive proprietà RestRequest.Method e RestRequest.Resource. Una caratteristica di RestSharp è quella di poter utilizzare, all interno della stringa che definisce la risorsa, dei segmenti identificati da nomi circondati da graffe, che possono essere successivamente rimpiazzati dai valori desiderati: var request = new RestRequest("/webapi/italy/region/cod_istat", Method.GET); // sostituisce il segmento cod_istat col valore effettivo request.addurlsegment("cod_istat", 1234); Altra caratteristica molto comoda di RestSharp è quella di poter assegnare i parametri della richiesta e lasciare che sia RestSharp a gestirne l inserimento a seconda del tipo di metodo utilizzato. Ad esempio, nel caso di una GET, RestSharp aggiungerà tali parametri in coda all URL in quanto la GET utilizza tali parametri per definire la parte query 5 dell URL, mentre nel caso di un POST, RestSharp aggiungerà i parametri nel body del messaggio. In entrabi i casi i parametri e i loro valori veranno correttamente tradotti (URLencoded). Inoltre con RestRequest.AddObject è molto facile aggiungere come parametri le proprietà pubbliche di un oggetto. Allo stesso modo, è assai semplice aggiungere eventuali headers HTTP aggiuntivi: Regione regione = new Regione CodIstat = "1234", Nome = "Lazio", SuperficieKmQuadrati = 138123 ; var request = new RestRequest("/webapi/italy/region", Method.POST); // Si possono inserire i parametri uno ad uno request.addparameter("cod_istat", regione.codistat); // oppure a partire da un elenco delle proprietà di un oggetto request.addobject(regione, "Nome", " SuperficieKmQuadrati"); // Oppure aggiungere tutte le proprietà pubbliche in lettura request.addobject(regione); // E facile aggiungere HTTP Headers request.addheader("header", "value"); // Ed è ugualmente facile aggiungere files da uploadare // (ovviamente funziona solo con i metodi HTTP che lo prevedono, come PUT e POST) request.addfile(path); Com è possibile notare dagli esempi di codice soprastanti, avendo un idea anche solo accennata del protocollo HTTP, le classi e i relativi metodi della libreria RestSharp sono piuttosto intuitivi e facili da usare. Maggiori informazioni sul sito ufficiale del progetto: http://restsharp.org/ Per provare ad utilizzarla nei vostri progetti, non vi rimane che scaricarla con NuGet: RestSharp. Happy coding! 5 Si veda a tal proposito lo schema URL riportato nella prima parte di quest articolo.