Using WCF to power Facebook feedstory forms
UPDATE: I think Facebook has changed the API so that links in full stories have to be split into src and href parts.
Several parts of the Facebook developer API requires application developers to furnish JSON web services that feed data to various actions. These include feed forms (“I want to stick something on my wall”) and the publisher to name a few. This post will descripe how that JSON can be generated through Windows Communication Foundation web services. You can see this code in action in the simple Louisefy application. Note that alternatives certainly exists (I recommend Json.NET), but now that we have WCF, why not use it?
(As Rune points out in a comment below, you can use the object-hierachy in the code to generate compliant JSON even if you’re using Json.NET instead of WCF).
As far as feed forms go, the developes pre-register a range of templates for the kind of stories they want their users. A set of templates that get fed to Feed.registerTemplateBundle might look like the following:
List oneLiners = new List() { "{*actor*} is <a href=\"{*linkurl*}\">{*linktitle*}</a> {*target*}s Facebook", }; List shortTmplts = new List() { new Dictionary() { {"template_title","{*actor*} is <a href=\"{*linkurl*}\">{*linktitle*}</a> {*target*}s Facebook"}, {"template_body","."} }, }; Dictionary fullTmplts = new Dictionary() { {"template_title","{*actor*} is <a href=\"{*linkurl*}\">{*linktitle*}</a> {*target*}s Facebook"}, {"template_body","{*swf*}"} };
The FBML markup to get a feed form that lets users publish on their friends feeds would look like this:
<form fbtype="multiFeedStory" action="http://link.to/myfeedhandler.svc"> Start typing names of your friends: <fb:multi-friend-input /> <input type="submit" label="Louisefy your friends feeds" /> </form>
The code for the actual feedhandler follows below, with the web.config stuff at the very bottom. I really like the terse, declarative syntax — there’s not a single hint that this all becomes JSON at some point. Note that if you need to pass arguments to your feedhandler, they’re very easy to map in the UriTemplate. I generally find WCF services to be fiendishly complex to get up and running because of the dearth of good tutorials and documentation — but once you get them going, it’s pure joy.
[ServiceContract] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class feedHandler { [WebInvoke(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, Method = "POST", UriTemplate = "/friendsfeed") ] [OperationContract] public FeedResponse GetResponse() { return new FeedResponse { method = "multiFeedStory", content = new Content { feed = new Feed { template_id = "1234", template_data = new TemplData { swf = "", linktitle = "Louisefying", linkurl = "http://apps.facebook.com/louisefy/", images = new FeedImage[] { new FeedImage { src = "http://foo.bar/my.png", href = "http://apps.facebook.com/louisefy" } }, } }, next = "http://apps.facebook.com/louisefy", } }; } } [DataContract] public class FeedResponse { [DataMember] internal string method; [DataMember] internal Content content; } [DataContract] class Content { [DataMember] internal string next; [DataMember] internal Feed feed; } [DataContract] class Feed { [DataMember] internal string template_id; [DataMember] internal TemplData template_data; } [DataContract] class TemplData { [DataMember] internal FeedImage[] images; [DataMember] internal string swf; [DataMember] internal string linkurl; [DataMember] internal string linktitle; } [DataContract] class FeedImage { [DataMember] internal string src; [DataMember] internal string href; }
web.config:
<system.serviceModel> <behaviors> <endpointBehaviors> <behavior name="Popcorn.feedHandlerAspNetAjaxBehavior"> <webHttp /> </behavior> </endpointBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> <services> <service name="Popcorn.feedHandler"> <endpoint address="" behaviorConfiguration="Popcorn.feedHandlerAspNetAjaxBehavior" binding="webHttpBinding" contract="Popcorn.feedHandler" /> </service> </services> </system.serviceModel>