Aug 11 2009

Standardizing JSON Responses in ASP.NET MVC

Category: ASP.NET | MVC | liteGridMatt @ 04:05

ASP.NET MVC provides very nice support for returning JSON data, but my chief complaint with it is that it’s too flexible.  You can basically cram anything you want in it and trust that it will make it to the client script, which has lead to a complete lack of coherence among the various controllers in our big MVC application.  Some actions return a simple string that contains either “success” or “error” depending on whether or not the operation succeeded.  Others return true or false.  This is going to cause maintenance problems down the road, so we are taking a stab at standardizing things.  Right now, the idea is to create a class derived from JsonResult that exposes a few standard properties while at the same time maintaining the flexibility of the original JsonResult.  The common properties are “status”, a boolean that is true or false depending on success or failure of the requested action, and “message”, an optional string that can be set with additional details in the case of failure.  The custom result maintains the ability to insert additional properties into the JSON result via an object (anonymous or otherwise) as well as through adding key/value pairs explicitly.  The implementation is quite simple (commments removed for readability):

public class StandardJsonResult : JsonResult
{
    public string Message { get; set; }

    public bool Status { get; set; }

    public Dictionary<string, object> Properties { get; private set; }

    public StandardJsonResult()
    {
        Properties = new Dictionary<string, object>();
        Data = Properties;
        Status = true;
    }

    public StandardJsonResult(object data)
        : this()
    {
        IDictionary<string, object> properties = data.ToDictionary();

        foreach (var keyValue in properties)
        {
            Properties.Add(keyValue.Key, keyValue.Value);
        }
    }

    public StandardJsonResult(Exception ex) : this()
    {
        Status = false;
        Message = ex.Message;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        //Copy in standard properties
        Properties.Add("status", Status);

        if (Message != null)
        {
            Properties.Add("message", Message);
        }

        base.ExecuteResult(context);
    }
}

There are several constructors provided.  The one that takes an object as a parameter allows you to send anonymous objects or view models across the wire just like you can with a regular JsonResult object.  The values are copied from the object using the method I previous described, but it could also be done using a RouteDataDictionary.  The values are added to the Properties dictionary, which is actually assigned to the JsonResult’s Data property.  The key/value pairs in the dictionary are serialized to properties in the JSON output.  The ExecuteResult method is overridden so that the two standard properties can be added to the dictionary prior to JSON serialization.

This class can also be extended for other recurring scenarios.  For example, here is the custom result that feeds data into liteGrid:

public class LiteGridJsonResult : StandardJsonResult
{
    public Array DataItems { get; private set; }

    public LiteGridJsonResult(Array dataItems)
    {
        DataItems = dataItems;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        Properties.Add("dataItems", DataItems);

        base.ExecuteResult(context);
    }
}

This custom result simply extends the standard one with a collection of data items that will be rendered by the client. 

Thoughts or suggestions?

Tags:

blog comments powered by Disqus