Shorten your links using bitly.com - C#

by Slobodan Dokic 3. May 2012 22:35

Introduction

Most of you have already seen links like http://bit.ly/JxHVUx but did not have extra time to create links in your application. A lot of businesses are using services offered by sites like bitly.com if nothing else, to shorten post URL for Twitter. But, there is something else going on - the business want to know who is clicking on their posts, they want to know in which countries people are paying attention to their posts. Knowing how many hours after  initial posting the people will stop visiting your posts is not too valuable for most of us, but for the marketing departments all of these tiny bits of information can be very important.

One way to make a post to Twitter or Facebook is to use bitly.com web site that has been out there for a while. What they are doing is very simple but in the same time very smart and innovative  idea - they will shorten your link, post it to Twitter or Facebook and process every click on your post, assuming that you will have at least one link in your post and most of the time that is exactly the case.  Now you can see what's going on. It's great idea especially because link's (URL) length can consume most of Twitter allowed number of characters.

In order to use bitly.com you will need to open a account by signing up here. You will be assigned API key that we will use in a moment. We will start with the most basic GET HTTP request to bitly.com API:

http://api.bitly.com/v3/shorten?

There will be a couple parameters required - login, apiKey and longUrl  (the link you want to shorten). In the end you should have something like this:

http://api.bitly.com/v3/shorten?login=VALUE&apiKey=VALUE&longUrl=VALUE&format=xml}              (1)

These three values are required but there is one more that is not required but I find it very important. It's format of the response on your request. There are three options:

      • xml
      • json
      • text

One more thing before we send first request to bitly.com API. All longURL values must be encoded. You CANNOT just grab whatever link you have and paste it to your request. You will get back an error from bitly.com API.

Example: http://www.yahoo.com will become  http%3A%2F%2Fwww.yahoo.com

For this example just find some web site that will encode your URL. This web site is good enough: http://meyerweb.com/eric/tools/dencoder/.  Once when we start coding our request we will use HttpUtility.UrlEncode(.....) method in .NET.

I strongly suggest that, if you do not have it done already, download and install Fiddlerfree web debugger. It's a great tool to see what's going on between your request and server response. Now, create your request by replacing all bold VALUE with your real data  (1). Open Fiddler web debugger and click 'Composer' button. Paste your request and click execute. If you are guessing that you are sending your request to bitly.com, you are right. If everything is OK with your request, you should see response status 200 together with response from bitly.com. You should see something like this:

Slice 1

Your initial long URL became http://bit.ly/I2NMeu which is just around 15 chars in your Twitter message.

I forgot to say that, in order to post this to your Twitter or Facebook account, you will need to allow bitly.com to access your account(s). More likely you already allowed some web sites or phone apps to access your Twitter or Facebook. This works the same way. You need to authorize bitly.com to use Twitter or Facebook. It's easy to do but you will need to sign in to bitly.comand set it up by yourself.

We are ready now to start some coding.  If you visit bitly API  http://dev.bitly.com/api.html you will find more information in the case if you want to use bitly.com services in your web site or phone app you will need to register OAuth application with bitly.com. For most of the API requests you will need to provide your web site URL and callback page. Why do they need callback URL? Thy need, after they authenticate the client in your name, to send you access_token that your application will use to access user’s data.

Example

First we will create simple windows phone library that we will use in our windows phone app. As you will see when you become more familiar with bitly.com API, the responses will have some elements in common – status_code or status_txt for example. Other response data will change based on requested API. We could use dependency injection OOP design pattern to create response object. Another way would be to use “.NET templates” which I use often in cases like this.

using System.Xml.Linq;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Net;

namespace Bitly.Client
{
public class BitlyResponse<T> where T : IBase, new()
{
public StatusCode StatusCode { get; set; }
public string StatusText { get; set; }
public string ErrorMessage { get; set; }
public T Data { get; set; }

public BitlyResponse(T Data)
{
this.Data = Data;
}

public void Process(IEnumerable<XElement> results)
{
this.Data.ParseResponse(results);
}

public void ParseResponse(XDocument doc)
{
XElement errorCodeResponse = doc.Descendants("status_code").FirstOrDefault();
if (errorCodeResponse == null ||
!Enum.IsDefined(typeof(StatusCode),(int)Convert.ToInt32(errorCodeResponse.Value)))
{
this.StatusCode = StatusCode.Unspecified;
this.ErrorMessage = "Unable to parse bitly response.";
}
else
{
this.StatusCode = (StatusCode)Convert.ToInt32(errorCodeResponse.Value);
this.ErrorMessage = doc.Descendants("status_txt").FirstOrDefault().Value;

if (this.StatusCode == StatusCode.OK)
{
this.Process(doc.Descendants("data"));
}
}

// Another way of getting values from xml
// var x = from c in doc.Root.Element("data").Elements()
// where c.Name == "url" select c;
// XElement n = ((IEnumerable<XElement>)x).ElementAt(0);
// n.Value
}
}
}

     
IBase interface will define two methods that every class T must implement. Also, class T must have default constructor.

using System.Collections.Generic;
using System.Xml.Linq;


namespace Bitly.Client
{
public interface IBase
{
void ParseResponse(IEnumerable<XElement> results);
string GetRequestUrl(Account account);
}
}

The class T will define object that will be populated when we parse XML response returned from bitly.com API. Also, this object will have one property – RequestUrl that will be bitly.com API URL. In our first example this Url will be: http://api.bitly.com/v3/shorten?login={0}&apiKey={1}&longUrl={2}&format={3}    where {0}, {1}, {2} and {3} will be replaced with your values. These API URL could be written in configuration file of the library. Another way is just to create a class like BitlyRequest with some constants.

using System;

namespace Bitly.Client
{
public class BitlyRequest
{
public const string URL_1 =
@http://api.bitly.com/v3/shorten?login={0}&apiKey={1}&longUrl={2}&format={3};


public const string URL_2 =
@"https://api-ssl.bitly.com/v3/shorten?access_token={0}&longUrl={1}&format={2}";
}
}

 
For first request BitlyRequest.URL_1 we will create class below. For this particular requests, we don’t need to register our application with bitly.com which means we do not need to provide callback page.
 

using System;
using System.Net;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;

namespace Bitly.Client
{
public class Shorten : IBase
{
public string ShortUrl { get; set; }
public string Hash { get; set; }
public string GlobalHash { get; set; }
public string LongUrl { get; set; }
public string NewHash { get; set; }
public string RequestUrl { get; set; }

public Shorten()
{
this.RequestUrl = BitlyRequest.URL_1;
}

public void ParseResponse(IEnumerable<XElement> results)
{
if (results == null)
throw new Exception("Unable to extract the results from bitly.com response.");

var node = results.FirstOrDefault();
this.LongUrl = node.Descendants("long_url").SingleOrDefault().Value;
this.ShortUrl = node.Descendants("url").SingleOrDefault().Value;
this.GlobalHash = node.Descendants("global_hash").SingleOrDefault().Value;
this.Hash = node.Descendants("hash").SingleOrDefault().Value;
}

public string GetRequestUrl(Account account)
{
return string.Format(this.RequestUrl,
account.UserName,
account.ApiKey,
HttpUtility.UrlEncode(account.LongUrl),
account.Format.GetStringValue());
}
}
}

 
ParseResponse(…) method will parse response from bitly.com. I said before we will expect response to be in XML format. There are other two options – JSON and TEXT. You can see now that Fiddler example that we did before is handy here because of the XML response.
 
GetRequestUrl(….) method will format bitly API request with your data. You can store your bitly account data in configuration file.
 
Account class
using System;

namespace Bitly.Client
{
public class Account
{
public string UserName { get; set; }
public string ApiKey { get; set; }
public Format Format { get; set; }
public Domain Domain { get; set; }
public string LongUrl { get; set; }

public Account(string longUrl)
{
this.LongUrl = longUrl;
}
}
}

If we want to call another API url (see BitlyRequest.URL_2) we must create another class T that will implement IBase interface. You will ask why we are doing all of these? It’s because every API response is different with different data returned.
 
There is one more thing that you will find interesting. How many times did you ask how to get String value of your Enum type? Here is a solution:
 
namespace Bitly.Client
{
public enum Format
{
xml,
json,
text
}

public static class Extension
{
public static string GetStringValue(this Enum value)
{
// Get the type
Type type = value.GetType();

// Get fieldinfo for this type
System.Reflection.FieldInfo fieldInfo = type.GetField(value.ToString());

// Return the first if there was a match.
return fieldInfo.Name;
}
}
}

 
The following code should be part of your windows phone app. Obviously, you will need to add a reference to your windows phone library. All HttpWebRequest calls must be asynchronous.
private void ShortenUrl(string longURL, string username, string apiKey)
{
BitlyResponse<Shorten> obj= new BitlyResponse<Shorten>(new Shorten());

Account ac = new Account(longURL);
ac.ApiKey = apiKey;
ac.Format = Format.xml;
ac.UserName = username;
ac.LongUrl = longURL;
string req = (obj.Data.GetRequestUrl(ac));
   HttpWebRequest request = (HttpWebRequest)WebRequest.Create(req);
AsyncCallback ar = new System.AsyncCallback(GetResponse);
request.BeginGetResponse(ar, request);
}

private void GetResponse(System.IAsyncResult result)
{
XDocument doc = null;
HttpWebResponse response = null;
BitlyResponse<Shorten> bitlyResponse =
new BitlyResponse<Shorten>(new Shorten());

if (result.IsCompleted)
{
try
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
response = (HttpWebResponse)request.EndGetResponse(result);
using (StreamReader reader =
new StreamReader(response.GetResponseStream()))
{
doc = XDocument.Load(reader.BaseStream);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
finally
{
if (response != null)
{
response.Close();
response.Dispose();
}
}

if (doc != null)
{
bitlyResponse.ParseResponse(doc);
}

}
}
 
This is most important part of previous example:
 

BitlyResponse<Shorten> bitlyResponse = new BitlyResponse<Shorten>(new Shorten());

You can see how we are using class T to inject appropriate object to BitlyResponse class. What we achieved is that we can easily add more class T objects without touching any other part of the code. Each class T implements parsing (IBase interface) of bitly.com API response.

If you have any questions feel free to email me.

 
 

Tags: ,

Windows Phone 7.1 | bitly

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

About me

My name is Slobodan Dokic, and I have been worrking as .NET developer for last seven years.

Month List

Calendar

<<  July 2014  >>
MoTuWeThFrSaSu
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar