SharePoint 2007 et WCF

April 14, 2010

SharePoint 2007 (tant WSS que Moss) propose un certain nombre de services web qui permettent de gérer un grand nombre de choses : lister les sites webs d’une collection de site, ajouter des éléments à une liste, etc.

Nous allons aborder ici comment exploiter ces services webs, d’abord au travers des références Web, bien connues et facile à utiliser, mais aussi au travers de WCF, plus complexe mais bien plus puissant.

Approche .Net 2.0 : création d’une référence Web

Avec l’approche .Net 2.0, il suffisait de créer une référence Web, d’instancier un proxy et d’appeler le code suivant pour par exemple lister les listes d’un site SharePoint. Prenons l’exemple d’une application console qui affiche les listes d’un mysite :

  1. référence vers http://my/personal/sbeauge
  2. Ajout du code suivant :
private static void ViaWebReference()
{
    var proxy = new Lists();
    proxy.UseDefaultCredentials = true;

    var listsXml = proxy.GetListCollection();

    var xmlnsMgr = new XmlNamespaceManager(listsXml.OwnerDocument.NameTable);
    xmlnsMgr.AddNamespace("sp", "http://schemas.microsoft.com/sharepoint/soap/");
    foreach (XmlNode listXml in listsXml.SelectNodes("sp:List", xmlnsMgr))
    {
        Console.WriteLine(
            "{0} / {1}",
            listXml.Attributes["Name"].Value,
            listXml.Attributes["Title"].Value
        );
    }
}

A l’éxécution, la console produira le résultat attendu :

{CAA9BAF6-0E17-4B83-8A08-A6D1C4A94896} / Documents partagés
{02AEA838-181D-41C2-A610-437410856C3E} / Documents personnels
{A79EF7F4-B5B9-45A8-AAD5-21F376A17D29} / Formulaires convertis
{45EFEA7C-BC26-4C50-89D0-AE68DE66B67C} / Galerie de composants WebPart
{D65D4849-64E8-4486-9AD5-A02A38A4B046} / Galerie de modèles de listes
{55A59BA0-0682-452C-B9CD-FCC408937B62} / Galerie de modèles de sites
{424ACBA5-53DA-409A-BFDB-8CBEB02883E4} / Galerie de pages maîtres
{36A629A8-C0BD-4D71-BFC9-B2E3E89D8958} / Images partagées
{0D033A5E-8C58-4CAC-8DC5-78E44D7AB8E1} / Liste d'informations utilisateur
{DD6EE02C-3B49-493A-906F-C2110F8BED8F} / Modèles de formulaire[/text]

Cette approche est tout à fait valable d’un point de vue technique, mais pose l’inconvénient d’un code en dur de la méthode d’authentification. L’approche WCF quant à elle s’appuie beaucoup plus sur une externalisation de la configuration dans le fichier de configuration, comme nous allons le voir.

Approche WCF

WCF a apporté beaucoup d’améliorations par rapport aux traditionnelles références web :

  • modularité plus importante : on peut étendre le fonctionnement en implémentant la plupart des couches de communication
  • support de plusieurs protocoles : http bien sûr, mais aussi tcp, canaux nommés et files d’attentes
  • support des services bi-directionnels (les clients peuvent rappeler le serveur)
  • externalisation de la configuration de la communication dans des fichiers de configuration, pour laisser le choix à l’administrateur du fonctionnement.

Ce dernier point est un point très important car il permet de réduire le nombre de lignes de code liés à la tuyauterie. Basiquement, le développeur décrit et implémente l’application et le format des échanges (les fameux contrats), et l’administrateur, via le fichier de config, branche alors les services depuis le fichier de config. Cela signifie que théoriquement, d’un environnement à l’autre, seul le fichier de configuration devra changer.

Accéder à un serveur SharePoint via WCF entre tout à fait dans cette optique… et je vais vous montrer comment dans le code C#, aucune ligne de code lié à la communication ne sera nécessaire.

La suite de l’article suppose que l’on est dans une configuration classique, c’est à dire en accédant à SharePoint en intranet et avec une authentification Windows intégrée.

Création du projet

Je vais pour cet exemple créer une application console.

Ajout d’une référence de service SharePoint

Avec Visual Studio 2008 ou 2010, vous pouvez créer une référence de service à partir  du menu contextuel du projet Add Service Reference.

Je saisie ici l’adresse de mon mysite et plus particulièrement du service web lists.asmx, puis je choisi un espace de nom.

Visual Studio génère alors la référence, avec la configuration WCF pré générée dans le fichier app.config. Seulement voila, cette configuration n’est pas adaptée comme le code suivant va le montrer.

Création de la méthode de recherche des listes dans le site

Le code C# suivant va vous permettre (ou du moins aurait du vous permettre) de lister les listes du site visé :

private static void ViaServiceReference()
{
    var proxy = new ListsSoapClient();

    var listsXml = proxy.GetListCollection();

    foreach (var listXml in listsXml.Elements("{http://schemas.microsoft.com/sharepoint/soap/}List"))
    {
        Console.WriteLine("{0} / {1}", listXml.Attribute("Name").Value, listXml.Attribute("Title").Value);
    }
}

Malheureusement, lorsque vous l’exécutez, vous aurez une belle erreur :

The HTTP request is unauthorized with client authentication scheme ‘Anonymous’. The authentication header received from the server was ‘NTLM’.

L’erreur annonce clairement que le serveur attendait une authentification Windows intégrée alors qu’on ne lui en fourni aucune.

Que faire dans ce cas ? L’objet proxy ne propose pas la propriété UseDefaultCredentials. Comment y remédier ?

En réalité, le code précédent est tout à faire correct. Le problème vient de la référence de service générée par Visual Studio qui ne correspond pas à cette configuration.

Si l’on observe le fichier de configuration app.config, vous verrez que Visual Studio a généré ce que j’appellerai un tas de truc qui au final ne correspond pas à ce que l’on va faire. Pour remédier au problème, il va falloir modifier le fichier de config, soit à la main, soit via l’outil d’édition de fichier de config fourni avec Visual Studio. Personnellement je préfère modifier le fichier de config à la main car l’outil d’édition est un poil plus lourd car il faut constamment naviguer entre les sessions.

Création d’un binding adapté

La première modification concerne le binding généré. Je vous conseille de vider complètement la configuration du binding en la remplaçant par la suivante (ou vous pouvez en créer une autre pour comparer) :

Avant :

<basicHttpBinding>
    <binding name="ListsSoap"
             closeTimeout="00:01:00"
             openTimeout="00:01:00"
             receiveTimeout="00:10:00"
             sendTimeout="00:01:00"
             allowCookies="false"
             bypassProxyOnLocal="false"
             hostNameComparisonMode="StrongWildcard"
             maxBufferSize="65536"
             maxBufferPoolSize="524288"
             maxReceivedMessageSize="65536"
             messageEncoding="Text"
             textEncoding="utf-8"
             transferMode="Buffered"
             useDefaultWebProxy="true">
        <readerQuotas maxDepth="32"
                      maxStringContentLength="8192"
                      maxArrayLength="16384"
                      maxBytesPerRead="4096"
                      maxNameTableCharCount="16384" />
        <security mode="None">
            <transport clientCredentialType="None"
                       proxyCredentialType="None"
                       realm="" />
            <message clientCredentialType="UserName"
                     algorithmSuite="Default" />
        </security>
    </binding>
</basicHttpBinding>

Après :

<basicHttpBinding>
  <binding name="SPSoap">
    <security mode="TransportCredentialOnly">
    <transport clientCredentialType="Ntlm"
              proxyCredentialType="Ntlm"
              realm="" />
    </security>
  </binding>
</basicHttpBinding>

Beaucoup plus simple non ?

Ce nouveau Binding est paramétré pour indiquer de sécuriser l’authentification (voir la documentation concernant l’énumération BasicHttpSecurityMode) et pour utiliser l’authentification intégrée Windows (NTLM).

Il suffit ensuite d’attacher ce Binding à l’EndPoint correspondant :

<client>
  <endpoint address="http://my/personal/steve/_vti_bin/lists.asmx"
            binding="basicHttpBinding"
            bindingConfiguration="SPSoap"
            contract="SharePoint.WebServices.Lists.ListsSoap"
            name="ListsSoap" />
</client>

Relancer ensuite l’application et cette fois ci le résultat attendu est bien là :

{CAA9BAF6-0E17-4B83-8A08-A6D1C4A94896} / Documents partagés
{02AEA838-181D-41C2-A610-437410856C3E} / Documents personnels
{A79EF7F4-B5B9-45A8-AAD5-21F376A17D29} / Formulaires convertis
{45EFEA7C-BC26-4C50-89D0-AE68DE66B67C} / Galerie de composants WebPart
{D65D4849-64E8-4486-9AD5-A02A38A4B046} / Galerie de modèles de listes
{55A59BA0-0682-452C-B9CD-FCC408937B62} / Galerie de modèles de sites
{424ACBA5-53DA-409A-BFDB-8CBEB02883E4} / Galerie de pages maîtres
{36A629A8-C0BD-4D71-BFC9-B2E3E89D8958} / Images partagées
{0D033A5E-8C58-4CAC-8DC5-78E44D7AB8E1} / Liste d'informations utilisateur
{DD6EE02C-3B49-493A-906F-C2110F8BED8F} / Modèles de formulaire

Cas particulier de l’exécution en local depuis un serveur

Si vous exécutez ce code localement depuis un serveur, il est possible que vous deviez ajouter le comportement d’endpoint suivant :

<behaviors>
  <endpointBehaviors>
    <behavior name="SPEndpointBehavior">
      <clientCredentials>
        <windows allowedImpersonationLevel="Impersonation" />
      </clientCredentials>
    </behavior>
  </endpointBehaviors>
</behaviors>

Puis de l’attacher à l’endpoint :

<client>
  <endpoint address="http://my/personal/steve/_vti_bin/lists.asmx"
            binding="basicHttpBinding"
            bindingConfiguration="SPSoap"
            behaviorConfiguration="SPEndpointBehavior"
            contract="SharePoint.WebServices.Lists.ListsSoap"
            name="ListsSoap" />
</client>

En effet, pour des raisons de sécurité sur un serveur, une application web ne permet pas l’accès en local à un service pour réduire le périmètre d’attaque potentiel en cas de piratage.

Conclusion

Il est vrai que l’utilisation de WCF est un peu plus complexe qu’une simple référence Web, mais j’espère que cet article vous aura permis de voir qu’utiliser WCF avec SharePoint est à la portée de tous. Vous pouvez ainsi bénéficier de toute la puissance de WCF en intégrant SharePoint dans des applications tierces plus complexes.

SharePoint 2010 apporte son lot de nouveautés (notamment les services Web Restfull), mais le parc de SharePoint 2007 déployé est encore important et le sera pendant un petit bout de temps. En attendant, n’hésitez pas à privilégier cette approche WCF !

Le fichier Hand.SharePointWCFSample.zip contient le projet exemple qui m’a servi pour l’article. Il faudra sans doute que vous modifiez le fichier de config pour changer la cible des références.