Blog de Julien Corioland (MVP)

Quand les technologies .NET deviennent passion...

Introduction à Windows Communication Foundation – Partie 3

Ce billet est le troisième de ma série d'articles dédiés à Windows Communication Foundation. Dans le premier, nous discutions des généralités des architectures orientées services et plus précisément de l'architecture même de WCF. Dans le second, nous mettions en place 3 des 5 entités fondamentales d'une application distribuée WCF : le contrat de service, le contrat de données et l'implémentation du service. Le code associé au deuxième billet est disponible ici.

Dans la suite, nous traiterons de la 4ème entité constituant l'architecture d'une application WCF : le service en tant que tel. Nous verrons donc comment héberger ce dernier sous diverses formes. La 5ème entité (le client Windows Communication Foundation) sera traitée dans le billet suivant.

Commençons tout de suite par traiter un premier cas de figure d'hébergement d'un service WCF, communément appelé « Self-Hosting ».

Héberger un service WCF dans une application Console

Le Self-Hosting consiste à héberger un service WCF dans une application .NET classique, qu'elle soit de type Console Application, Windows Forms Application, WPF Application ou encore Windows Service. Les 3 premières applications étant assez similaires, nous ne traiterons que le cas de l'application Console classique. En revanche, nous ferons un détour par les services Windows, bien que la technique de développement ne soit pas très éloignée.

Ce type d'hébergement se fait par le biais d'une classe – ServiceHost – définie dans l'espace de nom System.ServiceModel. Comme précédemment, il faudra veiller à ajouter la bibliothèque System.ServiceModel.dll aux références de notre application console. Afin de pouvoir héberger le service WCF et donc exécuter l'implémentation de ce dernier, définie dans mon précédent billet, nous devons également référencer deux autres librairies :

  • Le contrat de service : Northwind.ServiceContract.dll
  • L'implémentation du service : Northwind.WCFService.dll

La première étape à réaliser pour héberger le service WCF est d'instancier un objet de type ServiceHost. Nous utiliserons la surcharge de constructeur prenant en paramètres :

  • Un objet de type Type représentant le type de l'implémentation du service (Northwind.WCFService.NorthwindService)
  • Un tableau de paramètres variables représentant les adresses de bases de notre service.

Note : une adresse de base peut-être spécifiée pour chaque protocole (http, net.tcp…) afin d'éviter à entrer l'URI absolue lors de la configuration des endpoints par la suite. Le code ci-dessous illustre l'instanciation de notre hôte WCF :

ServiceHost host = newServiceHost(

typeof(NorthwindService),

newUri("net.tcp://localhost:1664/NorthwindService"),

newUri("http://localhost:1665/NorthwindService")

);

Le service sera donc disponible via les protocoles TCP et http aux adresses ci-dessus, respectivement. Nous devons à présent configurer les endpoints sur lesquels sera exposé le service. Pour cela, nous allons faire appel à la méthode AddServiceEndpoint de la classe ServiceHost, dont le constructeur prend en paramètre :

  • Une chaîne de caractères représentant le nom complet du contrat de service (ici : Northwind.ServiceContract.INorthwindService)
  • Une instance de classe dérivant de System.ServiceModel.Channels.Binding, par exemple :
    • BasicHttpBinding
    • NetTcpBinding
    • NetNamedPipeBinding
    • WsHttpBinding
  • L'adresse à laquelle le endpoint expose le service. L'adresse peut être absolue ou relative.

Ainsi, pour notre service exposé en TCP et en HTTP classique, nous obtenons :

host.AddServiceEndpoint(

"Northwind.ServiceContract.INorthwindService",

newNetTcpBinding(),

string.Empty

);

 

host.AddServiceEndpoint(

"Northwind.ServiceContract.INorthwindService",

newBasicHttpBinding(),

string.Empty

);

L'hôte est à présent configurée – très simplement – et prête à ouvrir un canal de communication pour écouter les appels WCF sur les deux points de terminaison définis. Pour se faire, vous devez faire appel à la méthode Open de la classe ServiceHost et tout simplement bloquer la Console à l'aide d'un appel à Console.ReadLine(), par exemple :

try

{

host.Open();

Console.WriteLine("Le service Northwind est à l'écoute...");

Console.WriteLine("Appuyer sur une touche pour quitter...");

Console.ReadLine();

}

catch (CommunicationException xcp)

{

Console.WriteLine("Une erreur est survenue lors de la communication WCF : {0}", xcp.Message);

}

finally

{

if (host != null && host.State == CommunicationState.Opened)

{

host.Close();

}

}

Remarquez que dans la clause finally, nous prenons le soin de vérifier que le canal de communication ne reste pas ouvert, et ce par le biais de la propriété State de ServiceHost, retournant l'état du canal et prenant une valeur de l'énumération System.ServiceModel.CommunicationState :

  • Closed
  • Closing
  • Created
  • Faulted
  • Opened
  • Opening

Si vous exécutez l'application, le service sera correctement hébergé et à l'écoute d'éventuels clients :

D'ailleurs si vous ouvrez votre navigateur favori à l'adresse http://localhost:1665/NorthwindService, vous verrez qu'un service WCF est bien présent. Nous nous intéresserons plus tard aux informations exposées sur cette page :

Jusqu'ici, nous n'avons rien écrit de bien compliqué comme code. En effet, l'exposition d'un service WCF est quelque chose de plutôt aisé. Cependant un gros problème ressort du code précédant : toutes les informations sur les endpoints du service WCF sont en dur dans le code de notre application. Si nous voulions changer ces informations, nous serions obligés de recompiler l'application.

Nous allons donc étudier comment écrire toute cette configuration dans le fichier App.config de notre application.

WCF propose une nouvelle section dans le fichier de configuration d'une application, qu'elle soit Web ou non : <system.serviceModel></system.serviceModel>. Cette balise contiendra – pour le côté hébergement du service – une balise <services></services> dans laquelle seront définis tous les services WCF hébergé par l'application, chacun au sein d'une balise <service></service>.Cette dernière balise sera décorée d'un attribut « name » représentant le nom complet de l'implémentation du service, ici Northwind.WCFService.NorthwindService :

<?xmlversion="1.0"encoding="utf-8" ?>

<configuration>

<system.serviceModel>

<services>

<servicename="Northwind.WCFService.NorthwindService">

 

</service>

</services>

</system.serviceModel>

</configuration>

C'est à présent au sein de la balise « service » que nous allons définir les adresses de base et endpoints sur lesquels NorthwindService sera exposé :

<servicename="Northwind.WCFService.NorthwindService">

<host>

<baseAddresses>

<addbaseAddress="http://localhost:1665/NorthwindService"/>

<addbaseAddress="net.tcp://localhost:1664/NorthwindService"/>

</baseAddresses>

</host>

<endpointaddress=""

binding="netTcpBinding"

contract="Northwind.ServiceContract.INorthwindService" />

<endpointaddress=""

binding="basicHttpBinding"

contract="Northwind.ServiceContract.INorthwindService" />

</service>

Remarquez que l'on retrouve clairement la définition de l'ABC du WCF vu dans le premier article de cette série dans la déclaration des endpoints.

Ainsi le code de notre hôte du service WCF se résume à :

ServiceHost host = newServiceHost(typeof(NorthwindService));

 

try

{

host.Open();

Console.WriteLine("Le service Northwind est à l'écoute...");

Console.WriteLine("Appuyer sur une touche pour quitter...");

Console.ReadLine();

}

catch (CommunicationException xcp)

{

Console.WriteLine(@"Une erreur est survenue lors

de la communication WCF : {0}", xcp.Message);

}

finally

{

if (host != null && host.State == CommunicationState.Opened)

{

host.Close();

}

}

Héberger un service WCF dans un service Windows

Ce type d'hébergement entre dans la catégorie du Self Hosting comme pour l'exemple précédant. Cependant, on remarquera quelques différences notoires tant au niveau du code que de l'installation. Pour créer un service Windows à l'aide du .NET Framework, il suffit de créer un projet de type « Service Windows » sous Visual Studio. Ce dernier génère alors le squelette du service, c'est-à-dire une classe dérivant de System.ServiceProcess.ServiceBase dans laquelle apparaît la surcharge de deux méthodes : OnStart et OnStop. Ces deux méthodes sont celles appelées par le système d'exploitation lors du démarrage et de l'arrêt du service, respectivement.

Avant même de s'intéresser au code de ce service, il est légitime de se poser une question : quels avantages avons-nous à utiliser un service Windows plutôt qu'une application .NET classique pour héberger notre service ?

Typiquement, une application classique ne peut-être que lancée lorsqu'une session est démarrée. On pourrait en effet imaginer de mettre notre application .NET dans les programmes à démarrer, mais l'idée de la session ouverte perdure. Contrairement, un service Windows, dès lors qu'il est installé sur le système, est entièrement géré par le système : démarrage automatique, utilisation du compte d'ordinateur local pour démarrer. Ainsi, l'administrateur n'a plus à s'occuper de quoi que ce soit, le service est complètement autonome et géré par le système, tout en offrant les mêmes possibilités que l'application Console réalisée ci-dessus.

Voici le code de notre service Windows, très similaire à ce que nous avons vu précédemment :

ServiceHost host;

 

protectedoverridevoid OnStart(string[] args)

{

try

{

host = newServiceHost(

typeof(Northwind.WCFService.NorthwindService)

);

host.Open();

}

catch (CommunicationException xcp)

{

Console.WriteLine(@"Une erreur est survenue

lors de la communication WCF : {0}", xcp.Message);

host.Abort();

}

}

 

protectedoverridevoid OnStop()

{

if (host != null && host.State == CommunicationState.Opened)

host.Close();

}

Notes :

  • Vous pouvez utiliser le même fichier de configuration App.config que celui que nous avions écrit dans la partie ci-dessus.
  • Comme précédemment, pensez à ajouter les librairies Northwind.ServiceContract.dll et Northwind.WCFService.dll aux références du service.

A présent, le code métier du service Windows est réalisé. Nous devons ajouter un installateur au projet afin de pouvoir installer le service sur le système. Pour se faire, rendez-vous dans la vue designer du service dans Visual Studio et faites un clic droit sur la surface pour choisir « Ajouter un installateur » :

Visual Studio ajoute alors deux objets à votre service :

  • serviceProcessInstaller1 : de type System.ServiceProcess.ServiceProcessInstaller, cet objet va nous permettre de configurer sous quel compte utilisateur sera exécuté le service. Nous choisissons ici le compte LocalSystem :

  • serviceInstaller1 : de type System.ServiceProcess.ServiceInstaller, cet objet va nous permettre de configurer le mode de démarrage du service, ainsi que de donner un nom et une description qui seront visibles dans la console de gestion des services de Windows. Nous choisissons de faire démarrer le service automatiquement avec le système :

Il ne reste plus qu'à compiler le service et à l'installer.

Pour se faire, nous allons utiliser un outil en ligne de commande – installutil.exe – disponible depuis l'invite de commande Visual Studio 2008. Cet outil s'utilise très simplement, en tapant la commande suivante :

C:\> installutil.exe -i chemin_executable_service

Note : chemin_executable_service représente le chemin d'accès vers l'exécutable dans le répertoire bin de votre projet Visual Studio.

Lorsque vous avez installez votre service, vous pouvez contrôler sa présence dans la console de gestion des services de Windows :

Vous remarquez que vous voyez bien apparaître le nom et la description entrés dans Visual Studio, et que le type de démarrage du service est bien défini à Automatique.

Note : pour désinstaller le service, vous devez utiliser la commande :

C:\> installutil.exe –u chemin_executable_service

Le service WCF est alors prêt à être consommé !

Héberger un service WCF sous IIS

IIS permet l'hébergement de service WCF sous diverses formes. Il faut cependant bien faire la distinction entre IIS (Simple) et IIS + Windows Activation Service (WAS). Nous traiterons WAS dans le point suivant.

Internet Information Service, de base, ne permet que l'hébergement de service Windows Communication Foundation présentant des bindings de type http au niveau de la configuration des endpoints. C'est bel et bien le seul protocole supporté pour les versions 5.0 et 6.0. Nous verrons dans la partie suivante dédiée à Windows Activation Service, que cette limitation est levée sous IIS 7.0, et donc sous Windows Vista et Windows Server 2008.

Avant de pouvoir héberger notre service WCF sous IIS, il faut activer les composants WCF pour l'activation HTTP. Pour se faire, le .NET Framework 3.0 fournit un utilitaire en ligne de commande – ServiceModelReg.exe – disponible dans le répertoire %windir%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation.

La commande suivante installe les composants par défaut :

C:\> %windir%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\ServiceModelReg.exe –i

Sachez que si vous êtes sous Vista ou Server 2008, les composants WAS dont nous parlerons plus loin seront automatiquement installés.

Sous Windows Vista, il est possible de réaliser cette installation de manière graphique, via l'ajout/suppression de composants Windows :

Une fois ceci fait, vous pouvez créer un nouveau projet de type Application Web dans Visual Studio 2008. Vous devez comme pour les deux cas précédant faire référence aux bibliothèques Northwind.ServiceContract.dll et Northwind.WCFService.dll.

Ajoutez ensuite un nouvel item de type Fichier texte à l'application Web. Nommez se fichier NorthwindService.svc. L'extension « svc », diminutif de service, permettra à IIS de charger le bon filtre permettant de traiter la requête comme un appel de service WCF. L'appel à l'outil ServiceModelReg.exe réalisé plus haut a – entre autre – installé un handler capable de gérer les fichiers .svc sur IIS.

Avec WCF, une nouvelle directive est apparue dans les applications web : <%@ ServiceHost %>. On reconnait ici le type ServiceHost que nous avions instancié dans les deux applications précédante. Il nous suffit alors simplement de préciser, via un attribut de cette balise – le type du service que nous souhaitons héberger :

<%@ServiceHostService="Northwind.WCFService.NorthwindService"%>

Il ne reste alors plus qu'à renseigner le fichier de configuration Web.config pour exposer notre service. Attention, rappelez vous que seul les bindings de type http sont possibles si vous n'avez pas activé Windows Activation Service. La structure du fichier Web.config pour la configuration WCF est identique à celle que nous avons vue précédemment :

<system.serviceModel>

<services>

<servicename="Northwind.WCFService.NorthwindService">

<endpointaddress=""

binding="basicHttpBinding"

contract="Northwind.ServiceContract.INorthwindService" />

</service>

</services>

</system.serviceModel>

Vous pouvez à présent déployer le service sous IIS et y accéder à l'adresse http://uri_du_site/NorthwindService.svc. Le service WCF est prêt à être consommé !

Windows Activation Service

Windows Activation Service est un nouveau composant disponible sous IIS 7.0 permettant d'héberger des services WCF en utilisant d'autres protocoles que le protocole HTTP. En effet, il va être possible d'effectuer une liaison entre le pipeline HTTP de IIS (qui traite les requêtes) vers d'autres composants supportant les protocoles TCP, IPC ou encore MSMQ, par exemple.

Pour cela, vous allez devoir activer les composants Windows Communication Foundation Non-HTTP Activation sous Windows Vista ou Server 2008. Pour se faire, vous pouvez soit utiliser l'outil ServiceModelReg.exe ou l'ajout/suppression de composants Windows comme vu précédemment :

Une fois les composants activé, il faut utiliser l'outil en ligne de commande – appcmd.exe – afin d'une part de mettre en place le binding entre le pipeline http et les composants WAS pour les protocoles utilisés, mais aussi activé ces protocoles sur le site web dans lequel nous hébergeons un service WCF. La première opération se fait donc au niveau du site web IIS et la seconde, à l'échelle de l'application Web. Cet utilitaire ce trouve dans le répertoire %windir%\System32\inetsrv :

  1. Mise en place du binding pour le protocole TCP

    C:\> %windir%\system32\inetsrv\appcmd.exe set site "Default Web Site" -+bindings.[protocol='net.tcp',bindingInformation='808:*']

    Note : ici, on ajoute une liaison de site de type net.tcp au site web par défaut, sur le port TCP 808 (par défaut).

  2. Activation des protocoles sur l'application web

     

    C:\> %windir%\system32\inetsrv\appcmd.exe set app "Default Web Site/NorthwindService" /enabledProtocols:http,net.tcp

     

    Note : ici on active le protocole net.tcp sur l'application web NorthwindService du site web par défaut.

A présent vous pouvez exposer votre service WCF sous des bindings mettant en jeu des protocoles différents de HTTP. Vous noterez que le code en tant que tel de l'application web n'a pas besoin d'être modifié comparé au code d'hébergement IIS classique vu dans la section précédante.

Exposer les métadonnées d'un service WCF

Jusqu'à présent, nous avons héberger notre service sous les diverses formes possibles (application console, service Windows, IIS, WAS) et sous différents protocole (http,tcp…). Nous nous sommes pourtant pas du tout intéressés aux métadonnées du service.

Si vous voulez maintenant consommer le service WCF vous pouvez tout à faire le faire, mais à l'aide d'un client Windows Communication Foundation uniquement, donc une application .NET uniquement. Or, nous avions dit que l'un des avantages de WCF était l'interopérabilité avec d'autre technologies.

Pour pouvoir être interopérable, votre service doit exposer ce que l'on appel des métadonnées, c'est-à-dire des informations sur sa définition même : quelles sont les opérations disponibles ? Quels sont les contrats de données mis en jeu ? Avec la réponse à ces questions, il sera alors possible d'écrire du code capable de consommer le service depuis des applications non .NET, en PHP ou encore en Java par exemple.

Il existe deux modes d'exposition des métadonnées d'un service WCF : WSDL ou MEX.

Web Service Description Language (WSDL)

Le WSDL est un langage dérivé du XML, bien connu des développeurs de services web classiques (ASMX). En effet, WSDL est un langage standardisé par le consortium du W3C, permettant aux services web d'exposer leurs métadonnées et donc d'être interopérable.

Pour qu'un service WCF se conduise de la sorte, nous allons devoir lui ajouter ce que l'on appel un comportement (behavior, en anglais). Une fois encore, nous n'avons en aucun cas à rajouter du code à notre implémentation. Tout se passe dans le fichier de configuration.

En effet, la balise <system.serviceModel></system.serviceModel> du fichier de configuration de votre hôte de service peut accueillir une balise <behaviors></behaviors> dans laquelle vous pourrez définir des comportements pour un endpoint (endpointBehaviors) ou pour un service (serviceBehaviors). Dans notre cas, c'est le deuxième qui nous intéresse.

Le comportement permettant à un service d'exposer ses métadonnées en WSDL est serviceMetadata, avec la propriété httpGetEnabled évaluée à True :

<behaviors>

<serviceBehaviors>

<behaviorname="MetadataWSDL">

<serviceMetadatahttpGetEnabled="true" />

</behavior>

</serviceBehaviors>

</behaviors>

Vous pourrez remarquer que nous donnons un nom à cette définition de comportement de service WCF. Nous devons à présent référencer ce comportement dans la déclaration du service à l'aide de l'attribut behaviorConfiguration de la balise <service></service> :

<services>

<servicename="Northwind.WCFService.NorthwindService"behaviorConfiguration="MetadataWSDL">

<endpointaddress=""

binding="basicHttpBinding"

contract="Northwind.ServiceContract.INorthwindService" />

</service>

</services>

A présent, si vous consultez via votre navigateur favori la page faisant référence au service WCF (voir partie Application Console), vous verrez que l'on vous propose un lien pour afficher les informations WSDL du service. Si vous suivez ce lui, vous obtiendrez une description du service NorthwindService, comme si c'était un simple service web ! Il devient donc consommable depuis d'autres langages que des langages .NET.

MetadataEXchange (MEX)

WS-Metadata Exchange est un protocole de description des services web publié par BEA Systems, IBM, Microsoft et SAP. Ce protocole descriptif est plus complet que WSDL (qui n'inclut que la structure du service et des données) alors que MEX prend en compte d'autres choses, comme les règles de sécurité du service, par exemple.

On pourrait penser qu'il suffit à nouveau d'ajouter un comportement à notre service pour lui permettre de publier ses métadonnées via MEX. Et bien non, cette opération va se faire par l'ajout d'un endpoint dans la configuration de l'hôte WCF.

Ce endpoint sera un peu particulier. Premièrement, il mettra en scène un binding de type mexHttpBinding. Deuxièmement, il ne mettra pas en jeu un contrat de service du type du contrat de service que l'on expose, mais de type IMetadataExchange. En effet, IMetadataExchange est un contrat de service, défini dans System.ServiceModel.Description assurant que le service implémente bien la technologie WS-MetadataExchange. Rassurez-vous, vous n'avez pas à implémenter ce contrat de service, le moteur WCF s'en charge pour vous !

Ainsi, voici un exemple fichier de configuration pour exposer notre service via MEX :

<system.serviceModel>

<services>

<servicename="Northwind.WCFService.NorthwindService"

behaviorConfiguration="MexBehavior">

<endpointaddress=""

binding="basicHttpBinding"

contract="Northwind.ServiceContract.INorthwindService" />

<endpointaddress="mex"

binding="mexHttpBinding"

contract="IMetadataExchange" />

</service>

</services>

<behaviors>

<serviceBehaviors>

<behaviorname="MexBehavior">

<serviceMetadata />

</behavior>

</serviceBehaviors>

</behaviors>

</system.serviceModel>

Ainsi, les métadonnées MEX sont disponibles à l'adresse : http://localhost/NorthwindService/NorthwindService.svc/mex.

Nous nous intéresserons en détails à l'outil svcutil.exe évoqué sur les deux images précédentes dans le billet suivant traitant de la consommation des services WCF.

Ce troisième post de la série consacrée à l'Introduction à Windows Communication Foundation s'achève ici. Pour mémoire, je vous rappel que nous avons vu les différents modes d'hébergement qui étaient possibles pour un service WCF :

  • Application .NET Classique et service Windows : utilisation de System.ServiceModel.ServiceHost
  • Internet Information Service : <%@ ServiceHost %>, http uniquement
  • Windows Activation Services : <%@ ServiceHost %>, pas de restriction des protocoles

Enfin, un service WCF peut exposer ses métadonnées sous deux formats :

  • WSDL (Web Service Description Language)
  • WS-Metadata Exchange (MEX)

J'espère que cette lecture vous en a appris plus sur l'hébergement de service Windows Communication Foundation. Je vous donne rendez-vous le plus tôt possible pour le prochain billet de cette série !

A bientôt. Wink

Sources : NorthwindWCF-Part3.zip (193.58 kb)

Posted: Jan 11 2009, 21:52 by julienc | Comments (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: CSharp | WCF