Monday, July 16, 2012

Making asynchronous Web API calls to a repository

"One of the most common ways in which recursion appears in daily life is when you postpone completing a task in favor of a simpler task, often of the same type."

It's as if Douglas Hofstadter was talking about the new .Net 4 Task type, introduced as part of the asynchronous .Net programming model, when he wrote this in "Gödel, Escher, Bach: an Eternal Golden Braid". There's a chapter in GEB called "Recursive Structures and Processes" which I had a look at again recently, having played around with a jQuery/Web API controller/repository stack for a site I'm doing.

I've been upgrading a lot of my old web services recently, code where I call out to the Flickr API, for instance, to load some photo-related data into one of my web pages. I felt it was time to get on board with the Web API framework, time to get RESTful. It's also time to start programming with the new Task type, introduced in .Net 4 specifically for asynchronous operations.

The starting point for asynchronous web operations is often the trusty jQuery $.ajax() operation, and that's where I'm starting from here too. Maybe it's just out of habit, given that I can do it relatively easily in straight .Net code now. Anyway, here's the JavaScript:
GetFeaturedWork: function () {
        dataType: "jsonp",
        url: "[FlickrUserId]&tags=LAGFrontPage"
    .done(function (data) {
    .fail(function () {
        // show an error message;

ShowFeaturedWork: function (data) {
    var work = {
        Name: data[0]["Name"],
        Url: data[0]["URL"]
    // whack the image HTML into a div on the page...
If you're familiar with jQuery Ajax calls, and are wondering about the unfamiliar-looking .done() and .fail() callbacks, there's an important "Deprecation Notice" in the documentation about the API change (soon) from v1.8 on, so heed ye well this notice.

Also, note the jsonp-osity of my cross-domain Ajax call. This is because I am calling from one domain to another which by default is not possible with vanilla JSON. All you need to do to circumvent this same origin policy restriction is to tell jQuery that the datatype of your request is jsonp, and to have a JsonpMediaTypeFormatter in your service app. Find out how to do it (JSONP with ASP.NET Web API) on StackOverflow. I also touched on jsonp in my post about creating a StackOverflow client.

Flickr photo
FlickrRussian Dolls, by Mike F.. 'Nesting, and variations on nesting...'

This is all straightforward so far, but the next bit I found tricky, so that's why I chose to write it up. It took me a while to work out how to get my Web API controller action to call a repository method asynchronously. It's not that hard, I suppose: I'm just new to the asynchronous methods in MVC4. I can't use the new await keyword as I don't want to upgrade to VS2012 yet - my OS won't support it, and while you can get a standalone download for ASP.Net 4.5 I wanted to see if I could do some new-fangled async programming without all that overhead. Also, in case you think that's copping out, I'd have to upgrade to the 4.5 sandbox on my hosting provider, so...all up, I don't need all that pain.

So, to recap, I'm starting from a page with a jQuery ajax call attached. This hits a controller - an apicontroller - action method, which calls out to a repository to hit the Flickr API.
// GET api/lavelleartgallery/photos?user={user}&tags={tags}
public List GetPhotos(string user, string tags)
    return _repository.GetPhotos(user, tags).Result;
Familiar looking, except for the .Result, I'm guessing. I'm using the "modern" HttpClient, rather than the older Microsft.HttpClient that I had been using. Which means I'm mainly concerned with the HttpResponseMessage that's returned. Or, in this case, a Task<HttpResponseMessage>, because it's asynchronous.
public Task<List<FlickrPhoto>> GetPhotos(string user, string tags)
    var requestUrl = string.Format("{0}&user_id={1}&tags={2}&extras=url_m&{3}", APIKey, user, tags, format);

    var client = new HttpClient();
    return client.GetAsync(requestUrl).ContinueWith(task =>
        // Get HTTP response from completed task. 
        var response = task.Result;

        // Check that response was successful or throw exception 

        return response.Content.ReadAsStringAsync().ContinueWith(readTask =>
            var photosets = JObject.Parse(readTask.Result);
            return photosets["photos"]["photo"].Children().Select(photo => 
                new FlickrPhoto
                    Id = (string)photo["id"],
                    Name = (string)photo["title"],
                    URL = (string)photo["url_m"]
Each of the two generic Task Async methods (client.GetAsync() and response.Content.ReadAsStringAsync()) pushes down a level on the stack, to use the lingo. "To push means to suspend operations on the task you're currently working on, without forgetting where you are - and to take up a new task." - GEB. The associated ContinueWith() continuation functions are what happens after the task pops - "to close operations on one level, and to resume operations exactly where you left off, one level higher". It's hard to envisage all this pushing and popping in the code as it stands in this pattern - the tabbing/indenting doesn't map well to the levels within the stack. This here is more indicative:
(jQuery) $.ajax() calls the (WebAPI) PhotosController
    => PhotosController calls the FlickrRepository
        => FlickrRepository calls the Flickr API
            => Flickr API returns JSON data
        <= FlickrRepository returns a (Task of) List of FlickrPhotos
    <= PhotosController returns a List of FlickrPhotos
<= $.ajax() returns JSON to the .done() callback...
By the way, the terms "push", "pop", and "stack" are basic recursion terminology, and come from the visual image of cafeteria trays. You push down, they pop back up again, etc. From now on, when you place a $5 chicken curry on your café tray you will immediately think of asynchrony, recursion and Russian dolls.

Anyway, I thought it was a useful way to think about the Asynchronous .Net 4 code you can use now, and anything that's gets us all talking about "Gödel, Escher, Bach" is worth it. I love GEB: it's a singular book. There's nothing at all like it, except maybe Alice in Wonderland.

1 comment:

  1. Doesn't calling ".Result" still block the thread, and defeat the purpose? I think your WebApi should return Task and not List. If not, you miss out on the actual benefit of using async tasks.