Go Back

Calling RESTful Facebook services with WCF

 

So, I got some crazy idea that I wanted to take another stab at writing a facebook application. A friend at work was showing off his WPF app that utilizes Netflix webservices and it gave me an idea about a facebook app. Unfortunately, someone already wrote my idea (http://apps.facebook.com/mynetflix ).

 

I got the idea of how to use wcf from this twitter app example on Kirk Evans Blog

(here's the code class library minus the config file Download Here)

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <bindings>

      <webHttpBinding>

        <binding name="FacebookConfig">

          <security mode="None"/>

        </binding>

      </webHttpBinding>

    </bindings>

    <client>

      <endpoint

        address="http://api.facebook.com/restserver.php"

        binding="webHttpBinding"

        bindingConfiguration="FacebookConfig"

        contract="Facebook.Service.IFacebookService"

        name="FacebookClient" />

    </client>

  </system.serviceModel>

</configuration>

 

 My service contract ended up looking like this after a few times banging on my head getting an error that said “No POST data to POST” when trying WebInvoke Attribute.

    [ServiceContract(Namespace=http://api.facebook.com/1.0/)]

    [XmlSerializerFormat(Style= OperationFormatStyle.Document,Use=OperationFormatUse.Literal)]

 

 

    public interface IFacebookService

    {

        [OperationContract]

          [WebGet(

            UriTemplate="?method=admin.getBannedUsers&api_key={apiKey}&sig={sig}&v=1.0",

            BodyStyle=WebMessageBodyStyle.Bare)]

        Message getBannedUsers(string apiKey, string sig);

 

        [OperationContract]

          [WebGet(

            UriTemplate="?method=admin.banUsers&api_key={apiKey}&sig={sig}&v=1.0&uids={userIds}",

            BodyStyle=WebMessageBodyStyle.Bare)]

        Message banUsers(string apiKey, string sigstring userIds);

 

        [OperationContract]

        [WebGet(

            UriTemplate="?method=admin.unbanUsers&api_key={apiKey}&sig={sig}&v=1.0&uids={userIds}",

            BodyStyle=WebMessageBodyStyle.Bare

            )]

        Message unbanUsers(string apiKey, string sig, string userIds);

 

    }

 

I chose these 3 methods because they do not require a session so I can just pass in my API key without needing a facebook signon to test my mad experiments.

 

So let’s move on to actually using this thing and the speed bumps I ran into there.

 

This is a work in progress, so far I’ve only done the “admin.unbanUsers” call

  public class FacebookCustomProxy

    {

        private readonly WebChannelFactory<IFacebookService> channelFactory;

        private readonly IFacebookService facebookChannel;

 

        public FacebookCustomProxy(string endpointName)

        {

            channelFactory =

                new WebChannelFactory<IFacebookService>(endpointName);

            facebookChannel = channelFactory.CreateChannel();

        }

 

        [Obsolete]

        public Message getBannedUsers(string apiKey, string sig)

        {

            return facebookChannel.getBannedUsers(apiKey, sig);

        }

        [Obsolete]

        public Message banUsers(string apiKey, string sig, string userIds)

        {

            return facebookChannel.banUsers(apiKey, sig, userIds);

        }

 

        public Message unbanUsers( long[] userIds)

        {

            Dictionary<string, string> args = new Dictionary<string, string>();

            args.Add("method", "admin.unbanUsers");

            args.Add("api_key", "<INSERT YOUR API_KEY HERE>");

            args.Add("uids",JasonMe(userIds));

            args.Add("v","1.0");

            string sig = MakeSig(args);

            return facebookChannel.unbanUsers(args["api_key"], sig, args["uids"]);

        }

 

        string JasonMe(long[] arrayOfLongs)

        {

            StringBuilder builder = new StringBuilder("[");

            bool adComma = false;

            foreach (long val in arrayOfLongs)

            {

                if (adComma)

                { builder.Append(","); }

                builder.Append(val.ToString());

                adComma = true;

            }

            builder.Append("]");

            return builder.ToString();

        }

 

 

         string MakeSig(IDictionary<string, string> parameters)

        {

            StringBuilder signatureBuilder = new StringBuilder();

 

            // Sort the keys of the method call in alphabetical order

            List<string> keyList = new List<string>(parameters.Keys);

            keyList.Sort();

 

            // Append all the parameters to the signature input paramaters

            foreach (string key in keyList)

                signatureBuilder.Append(String.Format(CultureInfo.InvariantCulture, "{0}={1}", key, parameters[key]));

 

            // Append the secret to the signature builder

            signatureBuilder.Append("<INSERT YOUR APP SECRET HERE>");

 

            MD5 md5 = MD5.Create();

            // Compute the MD5 hash of the signature builder

            byte[] hash = md5.ComputeHash(Encoding.UTF8.GetBytes(signatureBuilder.ToString().Trim()));

 

            // Reinitialize the signature builder to store the actual signature

            signatureBuilder = new StringBuilder();

 

            // Append the hash to the signature

            foreach (byte hashByte in hash)

                signatureBuilder.Append(hashByte.ToString("x2", CultureInfo.InvariantCulture));

 

            return signatureBuilder.ToString();

        }

    }

 

 

The first thing I ran into was I tried to get back just a string or a specific strongly typed object. But I kept running into problems with that. One, I couldn’t deserialize into a string, and two, sometimes I would get back <admin_unbanUsers_response> but sometimes I’d get back <error_response>. So I ended up using System.ServiceModel.Channels.Message class.

 

The second thing I hit was getting “Invalid Sig” errors from facebook.php. props for the MakeSig method to midowazzan on this facebook forum post

 

Lastly, I had issues because I wasn’t sending a JSON array to facebook. Unfortunately facebook doesn’t use a regular JSON array like this: {“user ids” : [378173,738127]} they use one that looks like this “[47387483,4783743]”, which is ok, just strange and you have no way of knowing without pouring through the awful wiki site.

 

 

Finally the code to actually execute this one method becomes trivial:

     FacebookCustomProxy proxy = new FacebookCustomProxy("FacebookClient");

           

            Message msg = proxy.unbanUsers( new long[]{789789,12344});

           

            string result = msg.ToString();

           

           

            Console.WriteLine(result);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Facebook DZone It! Digg It! StumbleUpon Technorati Del.icio.us NewsVine Reddit Blinklist Furl it!

Comments  2

  • Fatih YASAR 11/11/2009 12:00:00 AM

    Could you please, publish your example code or project with this post ?
  • james peckham 11/14/2009 12:00:00 AM

    OK, see above it's there now. you'll have to make your own config file as shown in the post.
Post a comment!
  1. Formatting options