Introduction▲
Cet article n'a pas pour vocation de vous faire un cours sur la syndication de contenu Web. Il existe déjà de nombreux articles traitant le sujet dont certains sont référencés à la fin de cet article. Nous allons néanmoins faire quelques petits rappels afin de mieux cerner la problématique.
Il existe aujourd'hui différents formats de syndication dont les plus connus sont RSS et Atom. La nouvelle API de syndication du Framework 3.5 prend en charge ces deux formats dans leur version 2.0 pour RSS et 1.0 pour Atom.
Regardons de plus près comment est constitué un flux RSS 2.0 :
<rss
version
=
"2.0"
>
<channel >
<title>
Titre du flux</title>
<link>
http://monflux.com</link>
<description>
Ceci est un exemple de flux</description>
<lastBuildDate>
Sat, 13 Oct 2007 17:29:38 Z</lastBuildDate>
<category
domain
=
"CategoryScheme"
>
Catégorie du flux</category>
<item>
<guid
isPermaLink
=
"false"
>
ItemID</guid>
<link>
http://monsite/items</link>
<title>
Titre de l'item</title>
<description>
Contenu de l'item</description>
</item>
</channel>
</rss>
Voyons maintenant un flux Atom 1.0 :
<feed
xml
:
lang
=
"en-us"
xmlns
=
"http://www.w3.org/2005/Atom"
>
<title
type
=
"text"
>
Titre du flux</title>
<subtitle
type
=
"text"
>
Exemple de flux Atom 1.0</subtitle>
<id>
FeedID</id>
<updated>
2007-08-13T17:29:38Z</updated>
<category
term
=
"FeedCategory"
label
=
"CategoryLabel"
scheme
=
"CategoryScheme"
/>
<logo>
http://monsite/image.jpg</logo>
<link
rel
=
"alternate"
type
=
"text/html"
title
=
"Link Title"
length
=
"1000"
href
=
"http://monsite/link"
/>
<entry>
<id>
ItemID</id>
<title
type
=
"text"
>
Titre de l'item</title>
<updated>
2007-08-13T17:29:38Z</updated>
<link
rel
=
"alternate"
href
=
"http://monsite/items"
/>
<content
type
=
"text"
>
Contenu de l'item</content>
</entry>
</feed>
Dans les deux cas, il ne s'agit que des quelques balises de base. Il en existe de nombreuses autres, mais encore une fois vous êtes invités à consulter les liens en fin d'article pour plus d'informations.
Comme vous le voyez, les deux schémas XML diffèrent totalement. Il vous faudra donc deux parseurs XML différents si vous voulez pouvoir lire un flux RSS et un flux Atom. De même, la publication d'un même contenu au travers d'un flux RSS et d'un flux Atom nécessitera deux blocs de codes différents, chacun créant les balises spécifiques pour chaque format.
Ainsi, ne serait-il pas intéressant d'avoir une API à qui l'on fournirait l'adresse d'un flux, qui se chargerait d'en reconnaitre le format (RSS 2.0 ou Atom 1.0), qui effectuerait le parsing nécessaire et qui mapperait le contenu vers des classes .NET facilement utilisables ?
De même, quel gain de temps appréciable serait de pouvoir fournir à cette API simplement le contenu que l'on souhaite voir exposer sous forme de flux, de lui indiquer le format souhaité (RSS 2.0 ou Atom 1.0) et de récupérer en sortie le flux formaté comme il faut.
Un code unique (ou presque) pour gérer deux formats différents, sympa non ? Et bien c'est ce que propose de faire la nouvelle API de syndication du Framework 3.5 : System.ServiceModel.Syndication.
Nous allons illustrer l'utilisation de cette API via une application Web, mais vous pouvez bien entendu vous en servir dans d'autres situations (une application WinForm par exemple).
I. Prérequis nécessaires▲
Vous aurez donc besoin du Framework 3.5 (encore Béta au moment de l'écriture de cet article) et de Visual Studio 2008 (pas obligatoire, mais conseillé) lui aussi encore en version Bêta.
Pour ajouter une référence à cette dll dans votre projet (sous Visual Studio): dans le menu projet, cliquez sur ajouter une référence. Si la dll System.ServiceModel ne se trouve pas dans l'onglet .Net, choisissez l'onglet Parcourir et allez la chercher dans C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5.
N'oubliez pas de référencer l'espace de noms System.ServiceModel.Syndication en utilisant la directive suivante :
using
System.
ServiceModel.
Syndication;
II. Consommation d'un flux▲
II-A. Chargement d'un flux▲
La classe la plus importante pour la gestion d'un flux se nomme SyndicationFeed. Cette classe représente un flux (RSS 2.0 ou Atom 1.0). Voici un diagramme simplifié de cette classe :
La première chose à faire pour consommer un flux est de créer une instance de SyndicationFeed correspondant à ce flux. Cependant nous n'allons pas utiliser le constructeur, mais la méthode static Load. Il existe trois surcharges de la méthode Load, mais celle qui nous intéresse ici est la version prenant en paramètre un objet de type Uri.
Ainsi, la création d'un objet SyndicationFeed permettant de lire le flux RSS 2.0 du site developpez.com s'effectuera de la façon suivante :
SyndicationFeed feed =
SyndicationFeed.
Load
(
new
Uri
(
"http://www.developpez.com/rss.php"
));
Vous pouvez ensuite accéder à différentes informations concernant le flux :
Response.
Write
(
"Titre: "
+
feed.
Title.
Text +
"<br />"
);
Response.
Write
(
"Description: "
+
feed.
Description.
Text +
"<br />"
);
Response.
Write
(
"Lien: "
+
feed.
Links.
First
(
).
Uri.
AbsoluteUri +
"<br />"
);
L'affichage correspondant :
Le site developpez.com possède aussi un flux Atom 1.0. Que faudrait-il modifier dans notre code précédent pour qu'il prenne en charge le flux Atom à la place du flux RSS ? Et bien pas grand-chose. Il suffit de modifier la construction de l'objet SyndicationFeed en passant l'adresse du flux Atom 1.0 à la place de celle du flux RSS 2.0 en paramètre de la méthode Load :
SyndicationFeed feed =
SyndicationFeed.
Load
(
new
Uri
(
"http://www.developpez.com/atom.php"
));
Et c'est tout ! Le reste du code reste strictement identique. L'API se charge d'extraire les informations demandées du flux. Par exemple, peu importe que la description du flux se trouve entre des balises description en RSS 2.0 ou entre des balises subtilte en Atom 1.0, l'API fera le parsing nécessaire et exposera le résultat via la propriété Description.
Attention, cette apparente facilité peut cacher quelques problèmes !
Certains sites proposent à la fois un flux RSS 2.0 et un flux Atom 1.0. Si vous produisez du code permettant de lire le flux RSS et que vous tentez de l'utiliser pour lire le flux Atom (ou inversement) vous risquez dans certains cas de vous retrouver avec quelques petites surprises. En effet, il n'est pas rare de voir certaines informations contenues dans le flux RSS ne pas être reportées dans le flux Atom (ou inversement). Vous pouvez donc vous retrouver avec des exceptions du type NullPointerException si vous effectuez des traitements sur des informations qui n'existent plus lors du changement de flux.
II-B. Lecture des éléments du flux▲
Nous sommes donc maintenant en mesure de charger un flux RSS 2.0 ou Atom 1.0 et d'en lire les différentes propriétés. C'est un bon début, mais nous n'allons pas aller bien loin avec ça. Nous allons donc maintenant nous intéresser à la récupération des différentes alertes, actualités, nouvelles diffusées au travers du flux.
Cette liste d'éléments est accessible via la propriété Itesm qui renvoie une liste (IEnumerable) d'objets de type SyndicationItem dont voici la liste des propriétés :
Cette classe représente donc un élément item dans le cas d'un flux RSS 2.0 et un élément entry dans le cas d'un flux Atom 1.0. Tout ce qui vous reste à faire c'est d'utiliser les propriétés de cet objet pour récupérer les informations voulues.
Voici un premier exemple dans lequel on récupère la liste des dix (au plus) dernières actualités publiées sur le site developpez.com et datant de moins de cinq jours. Pour chaque actualité on ne souhaite récupérer que son titre et sa date de publication et les afficher dans un GridView. Tout cela s'effectue en quelques lignes grâce à LINQ to Object :
var
items =
from
i in
feed.
Items.
Take
(
10
)
where
i.
PublishDate.
Date >=
DateTime.
Today.
AddDays
(-
5
)
select
new
{
Date =
i.
PublishDate.
ToShortDateString
(
),
Titre =
i.
Title.
Text
};
this
.
GridView1.
DataSource =
items;
this
.
GridView1.
DataBind
(
);
Et voici le résultat obtenu :
Facile non ?
III. Création d'un flux▲
III-A. Les bases▲
La première chose à faire ici est de créer un objet SyndicationFeed qui contiendra une liste d'objets SyndicationItem. Il existe six versions public du constructeur de SyndicationFeed. La différence se situe simplement au niveau du choix des champs à initialiser directement dans le constructeur.
Nous pouvons par exemple prendre le constructeur suivant qui prend en paramètre le titre du flux, sa description et son adresse :
SyndicationFeed feed =
new
SyndicationFeed
(
"Mon flux"
,
"Exemple d'un flux de syndication"
,
null
);
Il nous faut ensuite créer une liste de SyndicationItem et l'assigner à la propriété Items de l'objet SyndicationFeed que nous venons de construire :
List<
SyndicationItem>
items =
new
List<
SyndicationItem>(
);
// Création d'un nouveau Syndication Item.
SyndicationItem item =
new
SyndicationItem
(
"Un item"
,
"Contenu de l'item"
,
null
);
items.
Add
(
item);
// On continue à créer autant d'items que nécessaire
feed.
Items =
items;
Notre travail est maintenant terminé (ça n'a pas été bien long) et c'est maintenant à l'API de travailler un peu. Nous venons de lui fournir les données à exposer dans un flux de syndication; il ne reste plus qu'à lui demander de créer ce fameux flux.
Une seule instruction suffit pour récupérer un flux RSS 2.0 :
SyndicationFeedFormatter<
SyndicationFeed>
formatter =
new
Rss20FeedFormatter
(
feed);
Vous préférez un flux Atom 1.0 ? Pas de problème :
SyndicationFeedFormatter<
SyndicationFeed>
formatter =
new
tom10FeedFormatter
(
feed);
Facile !
Vous pouvez par exemple récupérer le code XML généré et l'enregistrer dans un fichier :
System.
Xml.
XmlWriter xmlw =
System.
Xml.
XmlWriter.
Create
(
@"C:\rss.xml"
);
formatter.
WriteTo
(
xmlw);
xmlw.
Close
(
);
Nos flux RSS et Atom sont maintenant créés; reste à les exposer à nos utilisateurs. Pour cela nous allons passer par un service WCF. Ne partez pas si vite, Visual Studio 2008 propose un template WCF spécialement dédié à la création de flux de syndication afin de vous simplifier (c'est peu dire) le travail: Syndication Service Library !
III-B. Le template Syndication Service Library▲
Créez donc un nouveau projet de type WCF (Framework 3.5) et sélectionnez Syndication Service Library. Le nom de ce template ne laisse aucun doute quant à son utilité.
Une fois le projet créé, son arborescence devrait ressembler à ceci :
L'interface IFeed1 correspond à l'interface définissant opérations exposées par le service :
namespace
SyndicationServiceLibrary1
{
// NOTE: If you change the interface name "IFeed1" here, you must also update the reference to "IFeed1" in App.config.
[ServiceContract]
[ServiceKnownType(
typeof
(Atom10FeedFormatter))]
[ServiceKnownType(
typeof
(Rss20FeedFormatter))]
public
interface
IFeed1
{
[OperationContract]
[WebGet(UriTemplate =
"*"
, BodyStyle = WebMessageBodyStyle.Bare)]
SyndicationFeedFormatter<
SyndicationFeed>
CreateFeed
(
);
// TODO: Add your service operations here
}
}
Elle ne possède qu'une seule méthode renvoyant un SyndicationFeedFormatter. Vous pouvez rajouter d'autres méthodes si vous le souhaitez, mais sa définition de base nous suffit dans notre cas.
La classe Feed1 implémente l'interface IFeed1. C'est dans cette classe que nous allons écrire le code permettant de générer nos flux RSS et Atom. Ce qu'il y a d'intéressant avec le template utilisé, c'est qu'il génère la classe Feed1 avec une implémentation complète et fonctionnelle de l'interface IFeed1. Voyez plutôt (vous devez reconnaitre le code, nous venons juste de le voir) :
namespace
SyndicationServiceLibrary1
{
public
class
Feed1 :
IFeed1
{
public
SyndicationFeedFormatter<
SyndicationFeed>
CreateFeed
(
)
{
// Create a new Syndication Feed.
SyndicationFeed feed =
new
SyndicationFeed
(
"Feed Title"
,
"A WCF Syndication Feed"
,
null
);
List<
SyndicationItem>
items =
new
List<
SyndicationItem>(
);
// Create a new Syndication Item.
SyndicationItem item =
new
SyndicationItem
(
"An item"
,
"Item content"
,
null
);
items.
Add
(
item);
feed.
Items =
items;
// Return ATOM or RSS based on query string
// rss -> http://localhost:8080/Feed1/
// atom -> http://localhost:8080/Feed1/?format=atom
string
query =
WebOperationContext.
Current.
IncomingRequest.
UriTemplateMatch.
QueryParameters[
"format"
];
SyndicationFeedFormatter<
SyndicationFeed>
formatter =
null
;
if
(
query ==
"atom"
)
{
formatter =
new
Atom10FeedFormatter
(
feed);
}
else
{
formatter =
new
Rss20FeedFormatter
(
feed);
}
return
formatter;
}
}
}
Le code permettant de créer un syndicationFeed et d'en tirer un flux RSS 2.0 et Atom 1.0 est déjà écrit ! Si vous compilez (sans rien toucher) le projet et lancez votre navigateur à l'adresse http://localhost:8080/Feed1/ (ou http://localhost:8080/Feed1/?format=atom pour le flux Atom) vous pourrez visualiser le flux RSS 2.0 d'exemple.
Notez au passage qu'un host pour votre service est créé par Visual Studio lorsque vous lancez un service WCF (un peu à la manière du mini serveur ASP.NET Cassini vous permettant de tester des pages ASP.NET sans avoir à utiliser IIS par exemple) :
N'oubliez pas de configurer le projet de la librairie comme « projet de démarrage ».
Nous construisons un projet de type Syndication Service Library (qui s'apparente à un projet de type WCF Service Library). Il existe deux types de template disponibles dans Visual Studio 2008: WCF Service Libraryet WCF Service Application(disponible dans la partie Web). Une librairie WCF va pouvoir être référencée et « hostée » dans divers types d'applications (WinForm, service Windows, IIS, etc). Cependant c'est à vous de créer ces hosts. Nous n'aborderons pas ici la création de ces hosts ou de projet WCF Service Application. Le host de test créé par Visual Studio suffira amplement pour l'instant.
Tout ce que nous avons à faire est d'ajouter notre propre code pour construire nos flux RSS et Atom. Nous allons par exemple créer un flux exposant la liste des différents processus en cours sur le serveur. Voici la méthode permettant de construire un objet SyndicationFeed correspondant à nos besoins :
public
SyndicationFeed GetProcesses
(
)
{
Process[]
processes =
Process.
GetProcesses
(
);
// Nouveau SyndicationFeed.
SyndicationFeed feed =
new
SyndicationFeed
(
);
feed.
Description =
SyndicationContent.
CreatePlaintextContent
(
"Processus en cours d'execution"
);
//Titre du flux
feed.
Title =
SyndicationContent.
CreatePlaintextContent
(
"Liste des processus"
);
feed.
Links.
Add
(
SyndicationLink.
CreateSelfLink
(
OperationContext.
Current.
IncomingMessageHeaders.
To));
//Création d'un item RSS/Atom pour chaque processus
feed.
Items =
from
p in
processes
orderby
p.
ProcessName
select
new
SyndicationItem
{
Title =
SyndicationContent.
CreatePlaintextContent
(
p.
ProcessName),
Summary =
new
TextSyndicationContent
(
String.
Format
(
"<b>{0}</b>"
,
p.
MainWindowTitle),
TextSyndicationContentKind.
Html)
PublishDate =
DateTime.
Now
};
return
feed;
}
Il suffit ensuite de modifier la fonction CreateFeed générée par le template afin de faire appel à notre fonction GetProcesses :
namespace
SyndicationServiceLibrary1
{
public
class
Feed1 :
IFeed1
{
public
SyndicationFeedFormatter<
SyndicationFeed>
CreateFeed
(
)
{
// Create a new Syndication Feed.
SyndicationFeed feed =
GetProcesses
(
);
// Return ATOM or RSS based on query string
// rss -> http://localhost:8080/Feed1/
// atom -> http://localhost:8080/Feed1/?format=atom
string
query =
WebOperationContext.
Current.
IncomingRequest.
UriTemplateMatch.
QueryParameters[
"format"
];
SyndicationFeedFormatter<
SyndicationFeed>
formatter =
null
;
if
(
query ==
"atom"
)
{
formatter =
new
Atom10FeedFormatter
(
feed);
}
else
{
formatter =
new
Rss20FeedFormatter
(
feed);
}
return
formatter;
}
}
}
Et voilà le travail ! Vous obtenez un flux RSS 2.0 et Atom 1.0 exposant la liste des processus en cours sur la machine. Si nous reprenons le code de lecture de flux que nous avions vu au tout début de cet article et que nous changeons simplement l'adresse du flux à lire, nous obtenons le résultat suivant :
Conclusion▲
Nous venons de voir comment s'abonner ainsi que créer des flux RSS 2.0 et Atom 1.0. La nouvelle API System.ServiceModel.Syndication rend la gestion de ces flux extrêmement facile. De plus, le template Syndication Service Library associé à WCF va vous permettre de facilement créer des flux de syndication. Vous n'avez donc plus d'excuse pour ne pas consommer ou même proposer des flux de syndication au sein de vos différents projets.
Liens▲
Introduction à Windows Communication Foundation par Ronald VASSEUR
Introduction à Windows Communication Foundation par Lainé Vincent
Remerciements▲
J'adresse ici tous mes remerciements à l'équipe de rédaction de « developpez.com » pour le temps qu'ils ont bien voulu passer à la correction et à l'amélioration de cet article.
Contact▲
Si vous constatez une erreur dans le tutoriel, dans les sources, dans la programmation ou pour toutes informations, n'hésitez pas à me contacter par le forum.