Getting Friends From Twitter With WCF

Today, we’ll show off a bit of WCF for working with Twitter.  The last time I posted on using Twitter with WCF, I showed how to update your Twitter status using WCF.  The rest of the Twitter API is just as easy to work with when you are using WCF.  Since the last post on that used an HTTP POST operation and used a configuration file for the update, this time we’ll use an HTTP GET operation and do everything in code. 

To make this plainly obvious, I am going to write a client application in .NET that uses WCF to call a RESTful service.  To frequent readers of my blog, this may sound obvious, but I get a fairly large number of emails about these posts that ask how would you call a non-.NET service.  Twitter is not written with .NET (that I know of, anyway), so this is an example of .NET calling a service written with some other language.

Defining the Operation Contract

First, just like last time, I will define the operation contract.  This is simply a method that maps to the pre-existing Twitter API.  We’ll use the statuses friends Twitter API, which requires an authenticated HTTP GET call.  Our HTTP GET will return data, so I have a choice to make.  I can either create a class that can be directly serialized, or I can use LINQ to XML (or some other XML parsing technology) to query it.  I will demonstrate the latter approach.

     [ServiceContract]
    public interface ITwitterStatus
    {
        [OperationContract]
        [WebGet(UriTemplate = "/statuses/friends.xml")]
        Message GetFriends();
    }

We create an interface, ITwitterStatus, that has a method GetFriends.  Neither of these names are significant, I could have called them ISesameStreet and WatchBigBird, there’s no correlation between the names and anything having to do with Twitter’s API.  What is important is the WebGetAttribute.  This attribute specifies the UriTemplate, which signifies the URL that is going to be called for the service.  It also indicates that we will be issuing an HTTP GET request.  The other important part to note is the return type, System.ServiceModel.Channels.Message.  This type will hold the data that is returned from HTTP request.

Calling the Service

Typically, when I show WCF code, I like to keep everything in configuration files.  However, I occasionally run across scenarios where someone prefers to write code instead of use configuration files.  OK, here’s a gimme for that group.  Since we are using the web programming model for WCF, we can leverage the WebChannelFactory and the WebHttpBinding types to make our call to Twitter.  The WebHttpBinding is what tells WCF to send the message as plain old XML encoded as UTF-8 text over an HTTP transport.  Twitter uses Basic authentication, so we need to add a few lines of code to tell WCF about our username and password for an HTTP Basic authenticated call.

 WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);
binding.MaxReceivedMessageSize = 99999999;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.Security.Transport.Realm = "Twitter API";

using (WebChannelFactory<ITwitterStatus> cf = new WebChannelFactory<ITwitterStatus>(binding, new Uri("https://www.twitter.com")))
{
    cf.Credentials.UserName.UserName = "your_twitter_username";
    cf.Credentials.UserName.Password = "your_twitter_password";
                    
    ITwitterStatus s = cf.CreateChannel();                
    Message m = s.GetFriends();

Easy enough.  We told WCF to only secure the transport (meaning don’t put credentials in the request body, just add HTTP level security stuff) and provided the username and password for HTTP Basic Auth.  Once we create the channel, we can call our GetFriends method and return back the object of type Message.

Querying the Results

In the past, I probably would have shown some odd serialization goo here, showing off what can be done with IXmlSerializable types.  However, I am really in love with LINQ to XML, so I’ll show that off.  The Message type has a method, GetReaderAtBodyContents that will return an XmlReader implementation.  We use the XmlReader-derived type to load an XDocument, and then query as usual with LINQ to XML.  The icing on the cake is that we can use anonymous types in C# to declare a type on the fly.  We don’t have to create a class to hold the properties for id, name, screen_name, location, etc., because C# knows to create one for us.

 var users = from user in XDocument.Load(m.GetReaderAtBodyContents()).Root.Elements("user")
              select new{
                    id = user.Element("id").Value,
                    name = user.Element("name").Value,
                    screen_name = user.Element("screen_name").Value,
                    location = user.Element("location").Value,
                    description = user.Element("description").Value,
                    profile_image_url = user.Element("profile_image_url").Value,
                    url = user.Element("url").Value,
                    followers_count = user.Element("followers_count").Value,
                    friends_count = user.Element("friends_count").Value,
                    status = user.Element("status").Element("text").Value
              };

foreach (var u in users)
{
    string s = string.Format("({0}) {1} - {2}", u.screen_name, u.name, u.status);
    System.Diagnostics.Debug.WriteLine(s);
}   

The Entire Solution

OK, let’s see the entire thing all at once.  This is just taking the code above and putting it all together.

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.ServiceModel.Channels;
using System.Xml.Linq;

namespace ConsoleApplication4
{
    [ServiceContract]
    public interface ITwitterStatus
    {
        [OperationContract]
        [WebGet(UriTemplate = "/statuses/friends.xml")]
        Message GetFriends();
    }

    class Program
    {
        static void Main(string[] args)
        {
            WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);
            binding.MaxReceivedMessageSize = 99999999;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
            binding.Security.Transport.Realm = "Twitter API";

            using (WebChannelFactory<ITwitterStatus> cf = new WebChannelFactory<ITwitterStatus>(binding, new Uri("https://www.twitter.com")))
            {
                cf.Credentials.UserName.UserName = "your_twitter_username";
                cf.Credentials.UserName.Password = "your_twitter_password";

                ITwitterStatus proxy = cf.CreateChannel();
                Message m = proxy.GetFriends();


                var users = from user in XDocument.Load(m.GetReaderAtBodyContents()).Root.Elements("user")
                            select new
                            {
                                id = user.Element("id").Value,
                                name = user.Element("name").Value,
                                screen_name = user.Element("screen_name").Value,
                                location = user.Element("location").Value,
                                description = user.Element("description").Value,
                                profile_image_url = user.Element("profile_image_url").Value,
                                url = user.Element("url").Value,
                                followers_count = user.Element("followers_count").Value,
                                friends_count = user.Element("friends_count").Value,
                                status = user.Element("status").Element("text").Value
                            };

                foreach (var u in users)
                {
                    string s = string.Format("({0}) {1} - {2}", u.screen_name, u.name, u.status);
                    System.Diagnostics.Debug.WriteLine(s);
                }
            }
        }
    }

}

That's it!  Pretty easy, huh?  I wrote this bit of code to use as part of a sample in an upcoming MSDN article, so look for it in the November issue of MSDN Magazine to see how I leveraged this!

For More Information

Twitter statuses friends REST API

How to update your Twitter status using WCF

WebChannelFactory Class

WebGetAttribute Class