Préambule
J'ai une grande passion dans la vie : que les systèmes interagissent entre eux, l'interopérabilité, l'échange de données, ce petit côté magique qui illumine mes neurones encore éveillés, malgré mes 30 et des bananes. Ce qui peut-être me fait porter parfois une casquette Système et Réseau
aux yeux de certains (ceci est un message à caractère non informatif et donc totalement incompréhensible).
Avec .NET 3.5, outre l'apport de Linq et autres joyeusetés, j'attendais avec impatience de pouvoir toucher du bout des doigts WCF (Windows Communication Foundation). Ce qui me parait intéressant, ce n'est pas les services Web SOAP ou le remoting, mais bien l'apport de REST dans WCF, dans sa forme apportée par le SP1 de .NET 3.5.
Il y aurait bien également MS ADO.NET Data Services mais bien trop proche de la base à mon goût et spécialisé dans le CRUD, ou alors pour une utilisation en intra (applications sur une même plateforme) ou in the Cloud. Cela pourrait être intéressant de s'y intéresser. Avec ADO.NET DS, on parlera également REST, avec les verbes HTTP GET, POST, PUT et DELETE. La querystring permet d'intégrer des expressions d'interrogation des données, une sorte de SQL mais pour REST, via l'URL.
Ensuite, on pourrait également me rétorquer : "mais tu n'as qu'à passer à ASP.NET MVC !! (ispice de nase)", auquel je réponds "Certes très cher" :
- ASP.NET MVC vient à peine d'entrer en RC1, et je ne suis pas sûr qu'il répondra à mon besoin,
- dans un lourd existant, il est toujours difficile de changer de modèle de développement Web : ASP.NET WebForms vers MVC, il est donc plus facile de s'intéresser à WCF qui ne nécessite pas en théorie de lourd changement,
- WCF, en plus de sa capacité à gérer le RESTful, est la synthèse de feu Remoting et des services Web SOAP, il est donc important de se pencher dessus,
- MVC ? tant qu'à changer de modèle, autant passer à Rails ou Merb,
Premiers pas, cela signifie introduction. Qu'offre WCF comme nouvelles classes qui pourraient nous être utiles ?
Quelques éléments
Avec WCF, les techniques Web 2.0 n'ont jamais été aussi accessibles, parmi lesquelles la syndication, REST, et la sérialisation directement en JSON.
Syndication
On trouvera dans le namespace System.ServiceModel.Syndication des classes et des providers disponibles pour la syndication de contenu, au format RSS 2.0 ou Atom 1.0, afin de consommer, ou créer des flux :
SyndicationFeedFormatter, SyndicationItem, SyndicationFeed, Rss20FeedFormatter, Atom10FeedFormatter
Cela aura le mérite de remplacer des librairies externes, de type RssToolkit ou - mieux - Argotic, ou tout simplement la création à la main, avec un XmlWriter, de flux RSS ou Atom.
Nous trouvons même des classes pour l'utilisation du format AtomPub, qui permet de publier des ressources, au travers d'Atom : AtomPub10CategoriesDocumentFormatter, AtomPub10ServiceDocumentFormatter - cela donne l'eau à la bouche, un jour.
A titre d'exemple, nous allons créer un flux d'éléments, que l'on exposera soit au format RSS, soit au format Atom. L'avantage est d'utiliser un type de plus haut niveau, SyndicationItem, ce qui lui confère une légère abstraction du format généré in fine, RSS ou Atom.
Très simple :
[Test] public void getRSS_Atom() { // flux RSS var strrss = new StringWriter(); XmlWriter xwrss = new XmlTextWriter(strrss); // flux Atom var stratom = new StringWriter(); XmlWriter xwatom = new XmlTextWriter(stratom); // création de 2 éléments dans le flux, sans format particulier pour l'instant var items = new List<SyndicationItem> { new SyndicationItem("WCF et la syndication", "Avec WCF, les techniques Web 2.0 n'ont jamais été aussi accessibles", new Uri("http://blog.olivier-duval.info/?post/WCF-premiers-pas"), "http://blog.olivier-duval.info/?post/WCF-premiers-pas", new DateTimeOffset(DateTime.Now)), new SyndicationItem("Linq to Xml (XLinq) vs. XPath", "Atom (voir RFC 4287) est un format ouvert de syndication et concurrent de RSS, qui lui, reste propriétaire (damned !). Le namespace utilisé dans le schéma XML d'Atom est http://www.w3.org/2005/Atom.", new Uri("http://blog.olivier-duval.info/?post/Linq-to-Xml-(XLinq)-vs.-XPath"), "http://blog.olivier-duval.info/?post/Linq-to-Xml-%28XLinq%29-vs.-XPath", new DateTimeOffset(new DateTime(2008,1,22))) }; // le flux dans lequel on injecte les éléments précédemment créés // on prend pour la date de màj, la date du dernier élément généré du flux // on pourra s'en servir pour gérer le cache navigateur du flux, grâce à l'ETag var feed = new SyndicationFeed { Title = new TextSyndicationContent("Le fil du zork"), Description = new TextSyndicationContent("un tout pour un rien"), Items = items, LastUpdatedTime = items.Max(x => x.LastUpdatedTime) }; // les providers disponibles : Rss ou Atom // veut-on du RSS ? feed.SaveAsRss20(xwrss); // ou de l'Atom ? feed.SaveAsAtom10(xwatom); // affichage du XML pour chacun des 2 formats Console.WriteLine("RSS : {0}", strrss.GetStringBuilder()); Console.WriteLine("ATOM : {0}", stratom.GetStringBuilder()); }
et comme on a appris dans le précédent billet à parcourir un flux Atom avec Linq, on pourra facilement afficher les éléments lus :
// création d'un XDocument à partir de la génération Atom var docatom = XDocument.Parse(stratom.GetStringBuilder().ToString()); XNamespace nsatom = "http://www.w3.org/2005/Atom"; var atomsx = from e in docatom.Descendants(nsatom + "entry") select new { Title = e.Element(nsatom + "title").Value, Description = e.Element(nsatom+"content").Value, Link = e.Elements(nsatom+"link"). Where(x=>x.Attribute("rel").Value=="alternate"). Select(y=>y.Attribute("href").Value). FirstOrDefault() }; foreach (var e in atomsx) Console.WriteLine("{0} ({1}) {2}", e.Title, e.Link, e.Resume);
System.ServiceModel.Syndication permet aussi tout simplement de consommer un flux en ligne :
var myreaderfeed = SyndicationFeed.Load(XmlReader.Create("http://feeds.olivier-duval.info/OlivierDUVAL")); // on prend les 10 derniers billets du flux // triés par la date de publication var ritems = from e in myreaderfeed.Items.Take(10) orderby e.PublishDate descending select new { Titre = e.Title.Text, Lien = e.Links.FirstOrDefault().Uri.ToString(), Resume = e.Summary.Text }; foreach(var i in ritems) Console.WriteLine("{0} {1} {2}", i.Titre, i.Lien, i.Resume);
Sérialisation JSON & RESTful
XML est le standard d'échange de données, compréhensible et interprétable par tous. Il peut être intéressant de renvoyer des données sérialisées au format JSON afin de faciliter son traitement directement en Javascript. On pensera notamment aux gadgets, widgets & consorts à apposer sur un site Web. Prochainement on s'intéressera à cette transformation, notamment grâce à la classe DataContractJsonSerializer, pour ensuite être lu directement en javascript.
Également, on verra dans un prochain billet, les rudiments d'un service WCF pour interroger et renvoyer au format XML ou JSON les résultats d'une requête GET. On poussera le vice à utiliser les verbes POST, PUT, DELETE, et peut-être HEAD du protocole HTTP.