Monday, December 12, 2011

A result message format for asynchronous web operations

Over the past year I've been working on developing SkillsLibrary as a cloud-based application for doing sports analysis through video on the web.  During that time I've learned a lot about the many and various challenges of building a modern web application.  Nowadays things such as mobility, page speed, and asynchronous operations rank high on the list of 'must have' requirements.   In this post I thought I'd touch on a simple little standard that I've used to help make the job of handling asynchronous operations simpler.

Let's start by looking at a typical piece of code for invoking an asynchronous operation:
$.post("/members/remove", data, function (result) 
{

}
This method is used to invoke a server side operation by passing a JSON data object to a controller action at the path '/members/remove'.  As we see from that code, the abstractions in modern Javascript libraries such as jQuery have made really simplified doing such operations.

The problem I set out to solve was to provide a consistent way to handle the response which comes back from the server.

Think about it for a moment... after we invoke the above operation, typically we will need to update a piece of user interface to reflect the result.  And that result may not have necessarily succeeded!  Let's imagine that the member we attempt to remove is not able to be removed because of a business rule.  Or if our logic fails and an exception occurs somewhere in the processing logic.  Or, perhaps it did succeed after all?

In SkillsLibrary I've developed a simple little 'interface' pattern for results which get returned from asynchronous operations which looks like this:
{
     bool Success;
     object Model;
     string Message;
}
Having a common structure for returning messages has a few benefits:
  1. You can create helper code to create the return messages
  2. You can create common client-side code for handling return messages
Focussing on the second of these benefits, think again about what can potentially happen when an async result is invoked.  In the first case, let's consider that the operation fails for some reason.  Your client-side code will need to know that the operation failed and be able to give the user some information about the failure.  In such a case it's as simple as querying the Success property and displaying the Message to the user like so:
$.post("/members/remove", data, function (result) 
{
    if( !result.Success )
    {
        UIHelper.displayErrorElement(result.Message);
    } else {
        ...
    }
}
Whereas, if the operation succeeds, we might want to do an operation to manipulate the DOM in some way to add or remove an object from the UI.  Again, this is easily achieved by using the Model property of the return message which will contain an object that represents a data item for the operation that was executed.
$.post("/members/remove", data, function (result) 
{
    if( !result.Success )
    {
        UIHelper.displayErrorElement(result.Message);
    } else {
        UIHelper.removeDOMItem($"#rootDOMElement", result.Model.Id);
    }
}
You can see that, having a consistent way of returning messages from the server we are able to streamline our client-side handling code which improves maintainability by having less code that is more readable.

No comments:

Post a Comment