/src/Amptools/Public/labels/Flickr.aspx
ASP.NET | 411 lines | 314 code | 97 blank | 0 comment | 13 complexity | 16febf6ab140b6f221d2f429fca1808a MD5 | raw file
1<%@ Page Title="amptools.net" Language="C#" MasterPageFile="~/App/Views/Layouts/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="System.Web.Mvc.ViewPage" %> 2<asp:Content ID="Content2" ContentPlaceHolderID="Head" runat="server"> 3 <link href='/Content/default/css/syntax-highlighter.css' rel="stylesheet" type="text/css" /> 4 <link rel="alternate" type="application/rss+xml" title="amptools.net - RSS" href="http://feeds.feedburner.com/amptoolsnet" /> 5 <link rel="service.post" type="application/atom+xml" title="amptools.net - Atom" href="http://www.blogger.com/feeds/7043853799247804655/posts/default" /> 6 <link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://www.blogger.com/rsd.g?blogID=7043853799247804655" /> 7 <style type="text/css"> 8 @import url("http://www.blogger.com/css/blog_controls.css"); 9 @import url("http://www.blogger.com/dyn-css/authorization.css?targetBlogID=7043853799247804655"); 10 </style> 11 12 <script type="text/javascript" src="http://www.blogger.com/js/backlink.js"></script> 13 <script type="text/javascript" src="http://www.blogger.com/js/backlink_control.js"></script> 14 <script type="text/javascript">var BL_backlinkURL = "http://www.blogger.com/dyn-js/backlink_count.js";var BL_blogId = "7043853799247804655";</script> 15</asp:Content> 16 17<asp:Content ID="Content3" ContentPlaceHolderID="Javascript" runat="server"> 18 <script type="text/javascript" src="~/javascripts/prototype.js"></script> 19 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shCore.js"></script> 20 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushCSharp.js"></script> 21 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushRuby.js"></script> 22 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushJScript.js"></script> 23 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushCss.js"></script> 24 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushSql.js"></script> 25 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushXml.js"></script> 26 <script type="text/javascript"> 27 //<[CDATA[ 28 dp.SyntaxHighlighter.HighlightAll('code'); 29 //]]> 30 </script> 31</asp:Content> 32<asp:Content ID="Content1" ContentPlaceHolderID="main" runat="server"> 33 34 35 <div class="left"> 36 <h2 class="silver"> 37 "Working on the complexity that is always indirectly implied by simpilicity." 38 </h2> 39 40 41 42 <h2 class="blog-title"><a name="8164722880411458481"></a> 43 44 Using WCF to access the Flickr JSON API 45 46 <span class="blog-byline"> 47 by: michael herndon, at: 7/17/2008 06:40:00 AM 48 </span> 49 50 </h2> 51 <div class="blog-content"> 52 <p>Even though there are quite a few opensource .Net libraries REST API out there, and a really impressive one for Flickr, they tend to never evolve or fork to use the newer framework technologies.  I think its great that they support 1.0, 2.0, and mono, but why not fork and support WCF?  I think part of the problem that .Net 3.5 use is not wide spread as it should be is not only information overload and bad naming for extension libraries, but also lack of opensource support to provide libraries and basis for current technology.  </p> <p>Flickr tends to be a little more tricky to use when calling it than some other REST APIs. WCF is a cool technology, but needs to be massaged when using it with Flickr due to a couple of gotchas and not so obvious things about WCF. In order to use Flickr, you need to register for an api_key and a shared secret code which is used to create an MD5 hash. You also need to create that MD5 each time you call a method, using the url parameters in a certain way. </p> <p>There are also a few not so obvious factors about using the JSON part of Flickr. All of the .Net libraries I've seen so far, use XML. While C# is king of parsing  XML, JSON tends to be more compact, which means less expense over the wire, though it might mean more time parsing once on the developers end point. So there are tradeoffs to using it. </p> <p>When I created the classes needed to use WCF to call Flickr, I have the actual "DataContract" object (DTO, Data Transfer Object), A Hash (Dictionary<string, object>) for adding the parameters to create the MD5 signature, A base class for creating the WCF proxy, a custom "WebContentTypeMapper", The "ServiceContract" interface, the actual "Client class", and a mixins class for creating the MD5. </p> <p>Here are some of the gotchas that I ran into while getting WCF to work with Flickr. </p> <p></p> <ol> <li>Flickr makes you create a md5 hash to send with every call for ALL parameters in alphabetical order using your "Shared Secret" Code as part of the md5.   </li> <li>Flickr's Json response "Content-Type" header is sent not as "application/json" but as "text/plain" which equates to "Raw" in WCF lingo.  </li> <li>The response object is wrapped and not obvious when it comes to json how the DataContract object should be formed.  </li> <li>Flickr will wrap the json response in a javascript function unless you pass in the parameter "nojsoncallback=1" </li> </ol> <p>To get around number one, I created an extension method that takes a Hash (Dictionary<string, object>) and adds method that converts the parameters into a MD5 string. </p> <pre class="code csharp">namespace Amplify.Linq 53{ 54 using System; 55 using System.Collections.Generic; 56 using System.Linq; 57 using System.Text; 58 59#if STANDALONE 60 public class Hash : Dictionary<string, object> 61 { 62 63 64 65 66 } 67#endif 68} 69// different file 70 71namespace Amplify.Linq 72{ 73 using System; 74 using System.Collections.Generic; 75 using System.Linq; 76 using System.Security.Cryptography; 77 using System.Text; 78 79 using Amplify.Linq; 80 81 public static class Mixins 82 { 83 84 85 86 public static string ToMD5(this Hash obj, string secret) 87 { 88 var query = from item in obj 89 orderby item.Key ascending 90 select item.Key + item.Value.ToString(); 91 92 StringBuilder builder = new StringBuilder(secret, 2048); 93 94 foreach (string item in query) 95 builder.Append(item); 96 97 MD5CryptoServiceProvider crypto = new MD5CryptoServiceProvider(); 98 byte[] bytes = Encoding.UTF8.GetBytes(builder.ToString()); 99 byte[] hashedBytes = crypto.ComputeHash(bytes, 0, bytes.Length); 100 return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(); 101 } 102 103 } 104}</pre> 105 106<p>Well Gotcha number two, took a bit of research to figure it out. The error I got was that it was looking for Json or XML but could not do anything with "Raw". So the solution is to create a custom binding with a custom WebContentTypeManager. </p> 107 108<pre class="code csharp">namespace Amplify.Flickr 109{ 110 using System.ServiceModel; 111 using System.ServiceModel.Channels; 112 using System.Runtime.Serialization; 113 using System.ServiceModel.Web; 114 115 public class JsonContentMapper : WebContentTypeMapper 116 { 117 public override WebContentFormat GetMessageFormatForContentType(string contentType) 118 { 119 return WebContentFormat.Json; 120 } 121 } 122} 123 124// different file 125 126namespace Amplify.Flickr 127{ 128 using System; 129 using System.Collections.Generic; 130 using System.Linq; 131 using System.Text; 132 using System.ServiceModel; 133 using System.ServiceModel.Channels; 134 using System.ServiceModel.Description; 135 using System.ServiceModel.Web; 136 137 using Amplify.Linq; 138 139 public class FlickrClient<T> 140 { 141 protected string Secret { get; set; } 142 143 protected string Key { get; set; } 144 145 protected string Token { get; set; } 146 147 protected string Url { get; set; } 148 149 protected T Proxy { get; set; } 150 151 152 public FlickrClient(string key, string secret) 153 { 154 this.Key = key; 155 this.Secret = secret; 156 this.Url = "http://api.flickr.com/services/rest"; 157 this.Proxy = this.InitializeProxy(); 158 } 159 160 public FlickrClient(string key, string secret, string token) 161 :this(key, secret) 162 { 163 this.Token = token; 164 } 165 166 protected virtual T InitializeProxy() 167 { 168 // using custom wrapper 169 CustomBinding binding = new CustomBinding(new WebHttpBinding()); 170 WebMessageEncodingBindingElement property = binding.Elements.Find<WebMessageEncodingBindingElement>(); 171 property.ContentTypeMapper = new JsonContentMapper(); 172 173 174 ChannelFactory<T> channel = new ChannelFactory<T>( 175 binding, this.Url); 176 177 channel.Endpoint.Behaviors.Add( new WebHttpBehavior()); 178 return channel.CreateChannel(); 179 } 180 181 } 182}</pre> 183 184<p>The 3rd issue, to know that all objects are wrapped in a "Response" object. To create a DataContract for the getFrob method on flickr, it took looking at the json to see what it was returning. The object below is the basic form of the Json object that is returned, it also includes the properties in case an error object is returned. </p> 185 186<pre class="code csharp"> using System; 187 using System.Collections.Generic; 188 using System.Linq; 189 using System.Text; 190 using System.Runtime.Serialization; 191 192 [DataContract] 193 public class FrobResponse 194 { 195 [DataMember(Name = "frob")] 196 public JsonObject Frob { get; set; } 197 198 [DataContract(Name = "frob")] 199 public class JsonObject 200 { 201 [DataMember(Name = "_content")] 202 public string Content { get; set; } 203 204 } 205 206 [DataMember(Name = "stat")] 207 public string Status { get; set; } 208 209 [DataMember(Name = "code")] 210 public int Code { get; set; } 211 212 [DataMember(Name = "message")] 213 public string Message { get; set; } 214 } 215 216 // different file 217 218 219 using System; 220 using System.Collections.Generic; 221 using System.Linq; 222 using System.Text; 223 using System.ServiceModel; 224 using System.ServiceModel.Web; 225 226 [ServiceContract] 227 public interface IAuthClient 228 { 229 [OperationContract, 230 WebInvoke( 231 UriTemplate = "/?method=flickr.auth.getFrob&format=json&nojsoncallback=1&api_key={key}&api_sig={signature}", 232 ResponseFormat = WebMessageFormat.Json, 233 BodyStyle = WebMessageBodyStyle.Bare)] 234 FrobResponse GetFrob(string signature, string key); 235 236 [OperationContract, 237 WebInvoke( 238 UriTemplate = "/?method=flickr.auth.getToken&format=json&nojsoncallback=1&api_key={key}&frob={frob}&api_sig={signature}", 239 ResponseFormat = WebMessageFormat.Json, 240 BodyStyle = WebMessageBodyStyle.WrappedResponse)] 241 Auth GetToken(string signature, string key, string frob); 242 } 243 244 245 // the actual client class. 246 247 using System; 248 using System.Collections.Generic; 249 using System.Linq; 250 using System.Text; 251 252 using Amplify.Linq; 253 254 255 public class AuthClient : FlickrClient<IAuthClient> 256 { 257 258 public AuthClient(string key, string secret) 259 :base(key, secret) 260 { } 261 262 public AuthClient(string key, string secret, string token) 263 :base(key, secret, token) 264 { } 265 266 public string GetFrob() 267 { 268 string md5 = new Hash() { 269 {"api_key", this.Key}, 270 {"format", "json"}, 271 {"method", "flickr.auth.getFrob"}, 272 {"nojsoncallback", 1} 273 }.ToMD5(this.Secret); 274 275 FrobResponse response = this.Proxy.GetFrob(md5, this.Key); 276 if (response.Code > 0) 277 throw new Exception(response.Message); 278 279 return response.Frob.Content; 280 } 281 }</pre> <p class="blogger-labels">Labels: <a rel='tag' href="http://www.amptools.net/labels/Amplify.aspx">Amplify</a>, <a rel='tag' href="http://www.amptools.net/labels/C#.aspx">C#</a>, <a rel='tag' href="http://www.amptools.net/labels/Code.aspx">Code</a>, <a rel='tag' href="http://www.amptools.net/labels/CSharp.aspx">CSharp</a>, <a rel='tag' href="http://www.amptools.net/labels/Flickr.aspx">Flickr</a>, <a rel='tag' href="http://www.amptools.net/labels/JSON.aspx">JSON</a>, <a rel='tag' href="http://www.amptools.net/labels/REST.aspx">REST</a>, <a rel='tag' href="http://www.amptools.net/labels/WCF.aspx">WCF</a></p> 282 </div> 283 <div class="blog-footer"> 284 <a class="comment-link" href="https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=8164722880411458481"location.href=https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=8164722880411458481;><span style="text-transform:lowercase">0 Comments</span></a> 285 286 <a class="comment-link" href="http://www.amptools.net/2008/07/using-wcf-to-access-flickr-json-api.aspx#links"><span style="text-transform:lowercase">Links to this post</span></a> 287 <span class="item-action"><a href="http://www.blogger.com/email-post.g?blogID=7043853799247804655&postID=8164722880411458481" title="Email Post"><img class="icon-action" alt="" src="http://www.blogger.com:80/img/icon18_email.gif" height="13" width="18"/></a></span> 288 289 <script type="text/javascript" src="http://w.sharethis.com/widget/?tabs=web%2Cemail&charset=utf-8&services=reddit%2Cdigg%2Cfacebook%2Cmyspace%2Cdelicious%2Cstumbleupon%2Ctechnorati%2Cpropeller%2Cblinklist%2Cmixx%2Cnewsvine%2Cgoogle_bmarks%2Cyahoo_myweb%2Cwindows_live%2Cfurl&style=rotate&publisher=baa2824f-9291-46c0-87b4-6d5a72508a05&headerbg=%23dddddd&inactivebg=%231bb601&inactivefg=%2370eb00%09&linkfg=%231bb601"></script> 290 </div> 291 292 </div> 293 <div class="right"> 294 <h3> 295 Connect 296 </h3> 297 <ul> 298 <li> 299 <a href="http://www.twitter.com/michaelherndon"> 300 <img src="/content/images/twitter-badge.png" alt="Twitter badge" /> 301 </a> 302 </li> 303 <li> 304 <a href="http://www.facebook.com/profile.php?id=824905532"> 305 <img src="/content/images/facebook-badge.png" alt="Facebook badge" /> 306 </a> 307 </li> 308 <li> 309 <a href="skype:michaelherndon?add"> 310 <img src="http://download.skype.com/share/skypebuttons/buttons/add_green_transparent_118x23.png" alt="Add me to Skype" /> 311 </a> 312 </li> 313 </ul> 314 315 <h3> 316 <a href="http://feeds.feedburner.com/amptoolsnet" rel="alternate" type="application/rss+xml"> 317 Subscribe in a reader <img src="http://www.feedburner.com/fb/images/pub/feed-icon32x32.png" style="vertical-align:middle" alt="" /> 318 </a> 319 </h3> 320 <ul> 321 <li> 322 323 </li> 324 <li> 325 <a href="http://feeds.feedburner.com/amptoolsnet"> 326 <img src="http://feeds.feedburner.com/~fc/amptoolsnet?bg=990066&fg=CCCCCC&anim=0" alt="" /> 327 </a> 328 </li> 329 <li> 330 <a href="http://add.my.yahoo.com/rss?url=http://feeds.feedburner.com/amptoolsnet" title="amptools.net"> 331 <img src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif" alt="" /> 332 </a> 333 </li> 334 <li> 335 <a href="http://fusion.google.com/add?feedurl=http://feeds.feedburner.com/amptoolsnet"> 336 <img src="http://buttons.googlesyndication.com/fusion/add.gif" alt="Add to Google Reader or Homepage"/> 337 </a> 338 </li> 339 <li> 340 <a href="http://www.pageflakes.com/subscribe.aspx?url=http://feeds.feedburner.com/amptoolsnet" title="Add to Pageflakes"> 341 <img src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&fileName=ATP_blu_91x17.gif" alt="Add to Pageflakes"/> 342 </a> 343 </li> 344 <li> 345 <a href="http://www.bloglines.com/sub/http://feeds.feedburner.com/amptoolsnet" title="amptools.net" type="application/rss+xml"> 346 <img src="http://www.bloglines.com/images/sub_modern11.gif" alt="Subscribe in Bloglines" /> 347 </a> 348 </li> 349 <li> 350 <a href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http://feeds.feedburner.com/amptoolsnet" title="amptools.net"> 351 <img src="http://www.newsgator.com/images/ngsub1.gif" alt="Subscribe in NewsGator Online" /> 352 </a> 353 </li> 354 </ul> 355 <h3 class="sidebar-title"> 356 Previous Posts 357 </h3> 358 <ul id="recently"> 359 360 <li><a href="http://www.amptools.net/2008/11/simple-log-in-amplify.aspx">Simple Log in Amplify </a></li> 361 362 <li><a href="http://www.amptools.net/2008/10/gallio-and-mb-unit-release-v304.aspx">Gallio and Mb-Unit release v3.0.4</a></li> 363 364 <li><a href="http://www.amptools.net/2008/10/moving-amplify-to-git-hub.aspx">Moving Amplify To Git Hub...</a></li> 365 366 <li><a href="http://www.amptools.net/2008/08/getting-datadirectory-folder-in-c-sharp.aspx">Getting the |DataDirectory| folder in C sharp</a></li> 367 368 <li><a href="http://www.amptools.net/2008/08/ampliy-fusion-javascript-libraries.aspx">Amplify-Fusion, JavaScript libraries compatibility...</a></li> 369 370 <li><a href="http://www.amptools.net/2008/07/amplify-wcf-twitter-api-client-library.aspx">Amplify's WCF Twitter API Client Library v0.2</a></li> 371 372 <li><a href="http://www.amptools.net/2008/07/use-c-to-determine-where-and-what.aspx">Use C# to determine where and what version of java...</a></li> 373 374 <li><a href="http://www.amptools.net/2008/07/using-wcf-to-access-flickr-json-api.aspx">Using WCF to access the Flickr JSON API</a></li> 375 376 <li><a href="http://www.amptools.net/2008/07/sample-mixins-for-httprequestbase-to.aspx">Sample Mixins for HttpRequestBase to use with Asp....</a></li> 377 378 <li><a href="http://www.amptools.net/2008/07/amplify-twittern-yet-another-c-twitter.aspx">Amplify's TwitterN, Yet Another C# Twitter REST AP...</a></li> 379 380 </ul> 381 <h3 class="sidebar-title"> 382 Archives 383 </h3> 384 <ul class="archive-list"> 385 386 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_03_30_index.aspx">03.30.2008</a></li> 387 388 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_04_06_index.aspx">04.06.2008</a></li> 389 390 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_06_08_index.aspx">06.08.2008</a></li> 391 392 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_07_06_index.aspx">07.06.2008</a></li> 393 394 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_07_13_index.aspx">07.13.2008</a></li> 395 396 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_07_20_index.aspx">07.20.2008</a></li> 397 398 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_07_27_index.aspx">07.27.2008</a></li> 399 400 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_08_03_index.aspx">08.03.2008</a></li> 401 402 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_08_10_index.aspx">08.10.2008</a></li> 403 404 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_10_26_index.aspx">10.26.2008</a></li> 405 406 <li><a href="http://www.amptools.net/blogs/michaelherndon/archives/2008_11_02_index.aspx">11.02.2008</a></li> 407 408 </ul> 409 </div> 410 411</asp:Content>