Wednesday 21 March 2012

Consuming Twitter Feeds in .NET using the Twitter REST API and JSON

My current client had a requirement to provide a twitter feed as part of a SharePoint 2010 intranet site. Unfortunately the RSS feed is no longer an actively supported mechanism for consuming Twitter feeds - instead the recommended way to get Tweets is via the REST APIs. The Twitter widgets were also not an option as I had to cache the results locally to reduce bandwidth consumed by the web part . I tried using LINQ2Twitter - and it worked well until I ran into problems with it not supporting an authenticated HTTP Proxy (ISA Server).

To get around this, I did the following:
  1. Performed a search via http://search.twitter.com using the query syntax e.g. from:news_com_au OR from:time  to get values from time and news_com_au Tweets
  2. Grabbed the querystring to use against the REST API. e.g. http://twitter.com/#!/search/from%3Anews_com_au%20OR%20from%3Atime%20
  3. Grabbed the search string from the above step and did a REST query specifying the JSON result format like so: http://search.twitter.com/search.json?q=from%3Anews_com_au%20OR%20from%3Atime%20
  4. With the JSON javascript that was downloaded, I created a JSON serialization class in C# using the great tool JSON2CSHARP - http://json2csharp.com/. This inferred the information from the JSON results that I provided from Twitter.
The class generated was as follows:
//Twitter JSon Result
namespace CompanyName.HRPortal.Repository.Dto.TwitterJsonResultDto
{
    public class Metadata
    {
        public string result_type { get; set; }
    }

    public class Result
    {
        public string created_at { get; set; }
        public string from_user { get; set; }
        public int from_user_id { get; set; }
        public string from_user_id_str { get; set; }
        public string from_user_name { get; set; }
        public object geo { get; set; }
        public object id { get; set; }
        public string id_str { get; set; }
        public string iso_language_code { get; set; }
        public Metadata metadata { get; set; }
        public string profile_image_url { get; set; }
        public string profile_image_url_https { get; set; }
        public string source { get; set; }
        public string text { get; set; }
        public object to_user { get; set; }
        public object to_user_id { get; set; }
        public object to_user_id_str { get; set; }
        public object to_user_name { get; set; }
    }

    public class RootObject
    {
        public double completed_in { get; set; }
        public long max_id { get; set; }
        public string max_id_str { get; set; }
        public int page { get; set; }
        public string query { get; set; }
        public string refresh_url { get; set; }
        public List results { get; set; }
        public int results_per_page { get; set; }
        public int since_id { get; set; }
        public string since_id_str { get; set; }
    }
}

I could then use the standard .NET System.Runtime.Serialization.Json.DataContractJsonSerializer to parse the results and expose it locally as a WCF service (using ASP.NET caching).

Here's the sample code to get the Twitter feed from behind a proxy using REST and JSON:

[TestMethod]
        public void TestGetPublicTweetsJson()
        {
            string feedUrl =
                "http://search.twitter.com/search.json?q=%09from%3Anews_com_au%20OR%20from%3Atime%20since%3A2012-03-20";
            HttpWebRequest httpWebRequest = (HttpWebRequest) HttpWebRequest.Create(feedUrl);
            httpWebRequest.Proxy = WebRequest.DefaultWebProxy;

            //Use The Thread's Credentials (Logged-In User's Credentials)
            if (httpWebRequest.Proxy != null)
                httpWebRequest.Proxy.Credentials = CredentialCache.DefaultCredentials;

            using (var httpWebResponse = (HttpWebResponse) httpWebRequest.GetResponse())
            {
                using (var responseStream = httpWebResponse.GetResponseStream())
                {
                    if (responseStream == null) return;
                    DataContractJsonSerializer jsonSerializer =
                        new DataContractJsonSerializer(typeof (RootObject));
                    RootObject root =
                        (RootObject) jsonSerializer.ReadObject(responseStream);
                }
            }
        }

Thats it!
DDK