次の方法で共有


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

Comments

  • Anonymous
    October 05, 2009
    Kirk, This is a great example.  I'd been depending on the WCF Rest Toolkit for this sort of data retrieval and hadn't thought through doing this by hand. It is also interesting that you used anonymous types in this example.  It seems like a great case for using the new dynamic type in order to break the code out into discrete pieces.   For instance, if you wanted to put this code into a view-model in an MVVM implementation.  WPF could consume your anonymous types if they are cast to object, I think, but you wouldn't be able to actually test the view-model unless the anonymous types are cast to dynamic type -- or fed into a custom type. Anyways, thanks for the post.  It opens up a lot of options for me -- especially since the Rest toolkit is still in "preview" lincesing.

  • Anonymous
    October 06, 2009
    I am new to WCF. I have read articles on how to use LINQ to XML to retrieve statues from twitter directly. So what's the benefit of using WCF here?

  • Anonymous
    October 07, 2009
    WCF is about distributed communications, and has facilities to support this like performance counters, WMI, diagnostics logging, and a number of other capabilities.