ASP.NET MVC Web Services (Part #1)

What do you think about ASP.NET MVC as platform for web service based applications? What about JSON Web Services? Look at the following code:

public class OrderPorcessingController
{
	public OrderPromotionResult PromoteOrder(string orderNumber)
	{
		var orders = new OrderRepository();
		var order = orders.GetOrderByNumber(orderNumber);

		order.PromoteOrder();

		return new OrderPromotionResult
		{
			NewStatus = order.Status
		};
	}
}

public class OrderPromotionResult
{
	public OrderStatus NewStatus { get; set; }
}

This code is actually ASP.NET MVC Controller. The only notable difference is return type. I return custom object. Not inherited from ActionResult. For me looks good.

For example, I can test it without any ASP.NET MVC bindings:

public void Should_change_status()
{
	var processing = new OrderPorcessingController();
	var result = processing.PromoteOrder("TestOrder");
	Assert.Equals(OrderStatus.Promoted, result.NewStatus);
}

And at the end I have endpoint /OrderPorcessing/PromoteOrder that accept argument orderNumber and return JSON.

Cool?

Implementation

Under the hood ASP.NET MVC, when action return type not inherited from ActionResult wraps it’s ToString() to ContentResult. Not so useful on the client side.

Of course, ASP.NET MVC has extensibility point that exactly fits. Well, not exactly true. It has at least two such points. Custom ControllerActionInvoker and no less custom IActionFilter. In our production code we use custom IActionFilter, however this post is about Custom ControllerActionInvoker.

Custom ControllerActionInvoker

public class ServiceActionInvoker : ControllerActionInvoker
{
	protected override ActionResult CreateActionResult(
		ControllerContext controllerContext,
		ActionDescriptor actionDescriptor,
		object actionReturnValue)
	{
		ActionResult actionResult = (actionReturnValue as ActionResult) ??
			new JsonResult { Data = actionReturnValue };

		return actionResult ?? new EmptyResult();
	}       
}

Yes, it overrides CreateActionResult and wraps result to the JsonResult instead of ContentResult. That it.

To use this stuff you need to override CreateActionInvoker.

protected override IActionInvoker CreateActionInvoker()
{
	return new ServiceActionInvoker();
}

Enjoy!

P.S. Code with example could be found at GitHub.