Lately, I’ve been working a lot with applications that expose JSON-based services to clients that care to consume their data. All of these services are nothing more than plain Controllers that return a ViewModel that’s rendered as JSON. You might be saying to yourself, “Big deal! All you’re doing is returning a JsonResult to the client! Tell us something new…” Well, as a matter of fact, that’s the whole purpose of this post. ;)

How Do Things Currently Work?

Right now within MVC, you have a explicit convention that a controller must follow for any action it exposes; they must return a type of ActionResult for the runtime to process it correctly.  However, if you have an action that returns a simple value type, ie. integers, bool, Guid, etc. the value assigned to the type is returned. In other words, if you have this controller:

The following is returned by the runtime:

contentresult_guid

Since the value is not an ActionResult, the runtime takes the value and converts its string representation. In this case, the Guid’s value is rendered out as a string on the browser. But what happens when we try to return a complex type, such as:

As you can see, it’s not quite what we expected:

contentresult_complex

What we want instead is to provide the value (and structure) of the complex type back to the caller as JSON…so how do we do it?

Extending the MVC Runtime

In the past I’ve blogged about inferred actions, where I discussed the usage of an ActionInvoker to handle the *dirty* work needed for this feature to work. To accomplish what we need in this case, again we call upon the power of the ActionInvoker (ControllerActionInvoker to be exact) along with an ActionResult, so it can hold the value (result) from our action.

The ControllerActionInvoker class has a method called CreateActionResult which is called after the action method is executed. It is within this method that ContentResult is created for the cases we stated earlier. So, let’s provide our own implementation, PocoInvoker, that does some of the masquerading of our complex type:

As you can see the logic is pretty simple:

  • If the actionResultValue is not an ActionResult…
    • Set it as the ViewData.Model value – in case we want to use it from within the View’s context
    • Create a new PocoResult to wrap the value and comply with the MVC runtime
  • If the actionResultValue is an ActionResult
    • Continue as normal!

From here, most of the work is done by the PocoResult class:

In this sample, the PocoResult class uses Newtonsoft’s JSON.NET as the JSON serializer to render out the complex value out to the caller.

As the comment in the source states, you can use the built JavaScriptSerializer or even inherit from JsonResult and omit all the extra work. I chose JSON.NET since that’s the JSON serializer I used across my projects.

Trying Out the Code

I created an application that interacts with simple person data:

poco_resuls_home

This application provides simple CRUD operations for creating a Person type.  If you look at the code for the Person and PersonController you will run into this little nugget:

The GetList action returns an IList<Person> which contains the values as specified above.  However, when this action method is called the list is returned as JSON:

poco_results_jsonlist

One very important thing to keep in mind is the concept of JSON Hijacking.  Phil Haack has a great post describing how this type of attack works and how ASP.NET MVC prevents these types of attacks from happening.  However, as Matt Hinze blogs, at times we might need to override this behavior for GET requests if we’re creating REST-like services (the purpose of this blog post). So we can implement a simple work around for types that are IEnumerable that wraps the result within a Data element in order to provide safe JSON:

safe_json

In the end, it’s up to you the developer to decide your comfort with these techniques and how best to apply them to your everyday work. I hope this post has provided some knowledge on how you can leverage the ASP.NET MVC runtime to it’s fullest.

Feel free to checkout the code out on github and ask any questions via comments.

Happy Coding!