Monday, April 11, 2011

How to consume a SharePoint Web Service with WCF Client

SharePoint Web Services are great because they can be called from any computer that can connect to SharePoint, not just the server that is running SharePoint. This is nice for installations of SharePoint like MOSS 2007 or 2010, but it is critical for SharePoint Online (BPOS) or SharePoint 360 where you will NOT have access to the server that SharePoint runs on. The problem is they are not nearly as easy to work with as just using the SharePoint object model, and they don’t expose everything through the web services. This is a real bummer! It is still a very useful alternative for situations where you cannot use the SharePoint Object Model.

There are a few tricks for working with SharePoint Web Services. There are lots of ways you can access the web services. I prefer to use WCF to access the services so that I get cool syntax from XElement.

Here is a list of web services that are available. Let’s use the Webs web service. Below are the step by step (close anyway) to using the web service in Visual Studio 2010.

  1. Create a console (for simplicity) or any other project type in Visual Studio 2010. NOTE: This can be on your laptop that is NOT running SharePoint.
  2. Go to the docs for the Webs Web Service. Here you will see that the url is: http://<Site>/_vti_bin/Webs.asmx you need to replace <Site> with your hostname or dns name, etc.
  3. Add a Service Reference to the above url (after you replaced <Site> with your site info). I am naming my MyWebs, but you can call it whatever you like, just make the appropriate changes to the code I show later.
  4. If you require all users to be authenticated (don’t accept anonymous users) then you will need to change the WCF bindings that were automatically created in your app.config such that it will pass security information to the web service. To do this, just search for the url of the web service. That will take you to a XML tag called endpoint. In that same XML tag you will see an attribute called WebsSoap. In this same app.config do a search for WebsSoap. This will take you to a binding tag. In that binding tag you will see a security tag. That security tag should have a mode=”None” attribute. Change mode=”None” to mode=”TransportCredentialOnly”. Then just below that you will see a transport tag, change the clientCredentialType=”None” to clientCredentialType=”Ntlm” and change the proxyCredentialType=”None” to proxyCredentialType=”Ntlm”
  5. All SharePoint services are work on Site Collections or Sites (webs) so really the docs should say http://<Site>/<subsite>/_vti_bin/Webs.asmx because if you are dealing with a subsite you need to use that url. You don’t need to add a service reference for each site, you can change it in the code. Be sure to change the endpoint address line in the example below.
  6. Since we are using WCF, we get XElement which gives us LINQ syntax and objects from XML. How cool is that! When determining what is available as attributes it is easiest to just look in the debugger (use the visualizer).
  7. Now that we the results as objects we can do whatever we want with them.

NOTE: You can also do this with the old Web Service client, but you don’t have XElement, LINQ support, but changing the URL is as easy as just changing the url and the Credentials property is all you need to change the credentials. You also don’t really have to worry about the WCF app.config stuff, but in the end I still like WCF.

 

private static void GetWebs()
{
    using (var ws = new MyWebs.WebsSoapClient())
    {
        // pass the proper credentials. Comment/Uncomment the proper lines depending on your situation
        ws.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials; // use current security context
        //ws.ClientCredentials.Windows.ClientCredential = new NetworkCredential("username", "password", "domain"); // use another Active Directory account
        //ws.ClientCredentials.Windows.ClientCredential = new NetworkCredential("username", "password"); // use account that is used for BPOS

        // change url to the site we want to work with
        ws.Endpoint.Address = new System.ServiceModel.EndpointAddress("
http://myhost/sites/SomeSiteHere/_vti_bin/Webs.asmx");

        // get all the webs at and below the specified site
        var results = ws.GetAllSubWebCollection();
               
        // use XElement and LINQ to get our results and create objects from the XML.
        var test = from r in results.Elements()
                    orderby r.Attribute("Url").Value
                    select new { Title = r.Attribute("Title").Value, Url = r.Attribute("Url").Value };

        // the results are just objects now, so do whatever you want with them.
        foreach (var item in test)
        {
            //Console.WriteLine(item.Url + " | " + item.Title);
            Console.WriteLine(item.Url);
        }

    }
}

1 comment:

Paul-Alexandre Naud said...

Nice post, thank you.
I just add this line:
ws.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

And increase maxBufferSize, maxReceivedMessageSize in the binding

It's working perfectly