Advertisement
ASP.NET

How to Build a Simple Twitter Widget with ASP.NET

by

In this tutorial, I'll be walking you through how to a write a Twitter widget for ASP.NET in the form of a reusable server control complete with nice things such as automatically turning URLs into links, and caching to speed up page load times.


Step 1 Getting Started

To follow this tutorial, all you need is Visual Studio (You can use MonoDevelop if you're not on Windows, although there's no guarantees there.) If you don't want to fork over cash for the full version of Visual Studio, you can grab the free Express Edition.

You'll also need knowledge of C# 3.0, as this tutorial makes use of some of the newer features of the language, such as lambda expressions and the var keyword.


Step 2 Creating the Control

ASP.NET includes a handy feature known as Server Controls. These are custom tags that aim to help developers structure their code. When a page using a server control is requested, the ASP.NET runtime executes the Render() method and includes the output in the final page.

Once you've created a new Web Application in Visual Studio, right click in the Solution Explorer and add a new item to the solution. Select ASP.NET Server Control, and give it a name. Here, I've called it Twidget.cs, but you're welcome to call it whatever you like. Paste the following code in, and don't worry if it all looks a bit foreign - I'll explain it all shortly.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.Script.Serialization;
using System.Net;

namespace WebApplication1
{
    public class Twidget : Control
    {
        public string Account { get; set; }
        public int Tweets { get; set; }

        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write("<ul>");

            foreach (var t in GetTweets().Take(Tweets))
                writer.Write("<li>{0}</li>", HttpUtility.HtmlEncode(t));

            writer.Write("</ul>");
        }

        public List<string> GetTweets()
        {
            var ls = new List<string>();

            var jss = new JavaScriptSerializer();
            var d = jss.Deserialize<List<Dictionary<string, object>>>(
                new WebClient()
                .DownloadString("http://api.twitter.com/1/statuses/user_timeline.json?screen_name=" + Account)
                );

            foreach (var x in d)
                ls.Add((string)x["text"]);

            return ls;
        }
    }
}

This is about as basic as you can get for a Twitter widget. Here's how it works:

When a user requests a page with this control on it, the Render() method gets executed with a HtmlTextWriter passed as a parameter. It writes out the <ul> tag, and then enters a loop which prints out each tweet as a list item. The magic here happens in the GetTweets() method. Notice how we are using the Take() extension method to make sure we only print the amount of tweets that we're asked to.

Once execution passes to the GetTweets() method, we setup a List>string< to hold our tweets and a JavaScriptSerializer to parse the JSON from the Twitter API servers. The statement on lines 31 - 34 (split up for readability) retrives the user timeline in JSON format, then deserializes into .NET types we can work with. On line 36, we loop through all the tweets and add them one by one to the tweet list. We have to manually cast x["text"] to a string because we deserialized it as an object. We had to do this, because the JSON returned by the Twitter API uses a smorgasboard of different types - which is fine for JavaScript, but a little tricky with C#.


Step 3 Using the Control

Now we have the code for our Twitter widget; let's put it to use! Open your Default.aspx page (or whichever page you want to use this in) and put the following code immediately after the <%@ Page %> directive:

<%@ Register TagPrefix="widgets" Namespace="WebApplication1" Assembly="WebApplication1" %>

Feel free to change the TagPrefix to whatever you like, but make sure that the Namespace attribute is correctly set to whatever namespace you placed the widget code in, and ensure that the Assembly attribute is set to the name of your web application (in our case, WebApplication1).

Once you've registered the proper tag prefix (and you'll need to do this for every page you want to use the control on), you can start using it. Paste the following code somewhere into your page, and once again, feel free to change the attributes to whatever you want:

<widgets:Twidget runat="server" Account="twitter" Tweets="10" />

If you've done everything properly, you should see a page similar to this when you run your web application:


Step 4 Some Fancy Stuff...

You've got to admit, the control we've got at the moment is pretty rudimentary. It doesn't have to be though, so let's spiffy it up a little by turning URLs into nice, clickable links for your visitors.

Find the foreach loop in the Render() method and scrap it completely. Replace it with this:

// you'll need to add this using directive to the top of the file:
using System.Text.RegularExpressions;

foreach (var t in GetTweets().Take(Tweets))
{
    string s = Regex.Replace(
        HttpUtility.HtmlEncode(t),
        @"[a-z]+://[^\s]+",
        x => "<a href='" + x.Value.Replace("'", "&quot;") + "'>" + x.Value + "</a>",
        RegexOptions.Compiled | RegexOptions.IgnoreCase
        );

    writer.Write("<li>{0}</li>", s);
}

It's all pretty much the same code, except for the humongous call to Regex.Replace() on line 6. I'll explain what this does.

The first parameter is the input, or the string that the Regex works on. In this case, it's just the tweet text after being passed through HttpUtility.HtmlEncode() so we don't fall victim to a vicious XSS attack. The input is then matched against the second parameter which is a regular expression designed to match a URL.

The third parameter is where it gets a little involved. This is a lambda expression, a feature new to C# 3. It's basically a very short way of writing a method like this:

public static string SomeFunction(Match x)
{
    return "<a href='" + x.Value.Replace("'", "&quot;") + "'>" + x.Value + "</a>";
}

All it does is wrap the URL with an <a> tag, of which all quotation marks in the URL are replace with the HTML entity &quot;, which helps prevent XSS attacks. The fourth and final parameter is just an ORed together pair of flags adjusting the way our regex behaves.

The output of the control after making this adjustment is somewhat similar to the screenshot below.


Step 5 Caching

There's a big problem with the code I've given to you above, and that is that it doesn't cache the response from the Twitter API. This means that every time someone loads your page, the server has to make a request to the Twitter API and wait for a response. This can slow down your page load time dramatically and can also leave you even more vulnerable to a Denial of Service attack. Thankfully, we can work around all this by implementing a cache.

Although the basic structure of the control's code remains after implementing caching, there's too many small changes to list, so I'll give you the full source and then - as usual - explain how it works.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.Script.Serialization;
using System.Net;
using System.Threading;
using System.Text.RegularExpressions;

namespace WebApplication1
{
    public class Twidget : Control
    {
        public string Account { get; set; }
        public int Tweets { get; set; }
        public int CacheTTL { get; set; }

        static Dictionary<string, CachedData<List<string>>> Cache = new Dictionary<string, CachedData<List<string>>>();

        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write("<ul>");

            foreach (var t in GetTweets().Take(Tweets))
            {
                string s = Regex.Replace(
                    HttpUtility.HtmlEncode(t),
                    @"[a-z]+://[^\s]+",
                    x => "<a href='" + x.Value.Replace("'", """) + "'>" + x.Value + "</a>",
                    RegexOptions.Compiled | RegexOptions.IgnoreCase
                    );

                writer.Write("<li>{0}</li>", s);
            }

            writer.Write("</ul>");
        }

        public List<string> GetTweets()
        {
            if (!Cache.Keys.Contains(Account)
                || (DateTime.Now - Cache[Account].Time).TotalSeconds > CacheTTL
                )
                new Thread(Update).Start(Account);
            
            if (!Cache.Keys.Contains(Account))
                return new List<string>();

            return Cache[Account].Data;
        }

        public static void Update(object acc)
        {
            try
            {
                string Account = (string)acc;

                var ls = new List<string>();

                var jss = new JavaScriptSerializer();
                var d = jss.Deserialize<List<Dictionary<string, object>>>(
                    new WebClient()
                    .DownloadString("http://api.twitter.com/1/statuses/user_timeline.json?screen_name=" + Account)
                    );

                foreach (var x in d)
                    ls.Add((string)x["text"]);

                if (!Cache.Keys.Contains(Account))
                    Cache.Add(Account, new CachedData<List<string>>());

                Cache[Account].Data = ls;
            }
            catch (Exception) { }
        }
    }

    class CachedData<T>
    {
        public DateTime Time { get; private set; }

        T data;
        public T Data {
            get {
                return data;
            }
            set
            {
                Time = DateTime.Now;
                data = value;
            }
        }
    }
}

As you can see, the Render() method remains unchanged, but there's some pretty drastic changes everywhere else. We've changed the GetTweets() method, added a new property (CacheTTL), added a private static field (Cache), and there's even a whole new class - CachedData.

The GetTweets() method is no longer responsible for talking to the API. Instead, it just returns the data already sitting in the cache. If it detects that the requested Twitter account hasn't been cached yet, or is out of date (you can specify how long it takes for the cache to expire in the CacheTTL attribute of the control), it will spawn a seperate thread to asynchronously update the tweet cache. Note that the entire body of the Update() method is enclosed in a try/catch block, as although an exception in the Page thread just displays an error message to the user, if an exception occurs in another thread, it will unwind all the way back up the stack and eventually crash the entire worker process responsible for serving your web application.

The tweet cache is implemented as a Dictionary<string, CachedData<string>>, where the username of the twitter account being cached is the key, and an instance of the CachedData<T> class is the value. The CachedData<T> class is a generic class and can therefore be used with any type (although in our case, we're only using it to cache a string.) It has two public properties, Data, which uses the data field behind the scenes, and Time, which is updated to the current time whenever the Data property is set.

You can use the following code in your page to use this caching version of the widget. Note that the new CacheTTL attribute sets the expiry (in seconds) of the tweet cache.

<widgets:Twidget runat="server" Account="twitter" Tweets="10" CacheTTL="600" />

Conclusion

I hope this tutorial has not only taught you how to make a Twitter widget, but has given you an insight into how server controls work as well as some best practices when 'mashing up' data from external sources. I realise that the browser output of this control isn't exactly the prettiest, but I felt that styling it and making it look pretty was outside the scope of the article and has therefore been left as an exercise for the reader. Thanks for reading! Feel free to ask any questions that you might have in the comments section below.

Related Posts
  • Code
    Web Development
    Laravel Unwrapped: Session, Auth and CacheLaravel wide retina preview
    Join me as we learn how to use Laravel's component-based system, Illuminate. Additionally, we'll see how to utilize service providers, Laravel's manager system, the Session, Auth, and Cache components, and the Store, Guard, and Repository libraries.Read More…
  • Code
    ASP.NET
    Code Generation Using T4Dotnet wide retina preview
    Learn the advantages and disadvantages of using code generation via T4 templates.Read More…
  • Code
    Web Development
    Stacks and QueuesData structures succinctly wide retina preview
    Learn about stacks and queues, a building block for creating solutions to business problems via algorithms and data structures.Read More…
  • Code
    ASP.NET
    Preventing Code InjectionCsrf dotnet retina preview
    Often, websites seem to exist primarily to put something into a database in order to pull it out later. While other database methods, such as NoSQL, have gained popularity in recent years, data for many websites still resides in the traditional SQL database. This data often consists of valuable personal information such as credit card numbers and other personal information of interest to identity thieves and criminals. Hackers therefore always look to get this data. One of the most common targets of these attacks is the SQL databases that lie behind many web applications through a process of SQL Injection.Read More…
  • Code
    JavaScript & AJAX
    Getting Into Ember.js: Part 5Getting into ember
    Editor's Note: The Ember.js team has shifted to an expedited release schedule and as of this publication date are on version 1.2.0. This tutorial was written pre-v1.0 but many of the concepts are still applicable. We do our best to commission timely content and these situations happen from time-to-time. We'll work to update this in the future. In part 3 of my Ember series, I showed you how you can interact with data using Ember's Ember.Object main base class to create objects that define the methods and properties that act as a wrapper for your data. Here's an example:Read More…
  • Code
    Scala
    Building Ribbit in ScalaRibbit scala retina preview
    In this tutorial we will implement the Ribbit application in Scala. We'll be covering how to install the Play web framework, a NetBeans plugin for it, and finally the code in Scala. If you are new to Scala, check out this previous tutorial which will help you set up your environment and provides you with a general platform that you can build upon. Even though the essence of Ribbit is to create/send/read Ribbits (our version of tweets), we will spend a large part of this tutorial explaining how Play works, authentication, and persistence. After these are in place, the rest becomes much easier. We will also implement ribbit creation, submission and listing out all ribbits. Following someone, advanced user settings, and direct messages will be an extra assignment for you to complete on your own. I am sure if you manage to follow along with this tutorial and create Ribbit as explained below, these three functionalities will be easily accomplished as homework.Read More…