In a world in which a web back-end is necessary to almost any application, the next big problem for every ASP.NET developer is to figure out the most appropriate way to build a public web API that both client HTML pages and mobile apps and sites can consume.
At the end of the day, what I pompously called “public HTTP API for data” here in the title is nothing more than a way to make a HTTP request and get some usable data. This is the same old core idea that web services brought in more than a decade ago. While the core idea remains unchanged, the actual tools you can use to deploy and use it are significantly changing.
We first had WCF as the sole option. Next up, we figured out we could more effectively use a plain ASP.NET MVC controller to do the job without the heavy tax represented by the WCF infrastructure. More recently we’re learning that the newest Web API uses a different and more parametric pipeline than ASP.NET MVC. While for the purposes of this article I stop here, I’m sure that some other options could be added to the list.
What’s the bottom line? How should you go building the much needed public web API for your site ecosystem? In particular, I’ll discuss how to expose the same data set via JSON and XML.
From WCF to Plain ASP.NET MVC Controller
In the beginning of interactive web (i.e., Ajax) there was just WCF for ASP.NET developers to build public HTTP-based endpoints to invoke via JavaScript and jQuery. WCF, however, was not simply conceived in order to serve HTTP requests. It was rather more ambitiously conceived to support SOAP and WS-* protocols over a wide variety of transportation layers. As you can see, HTTP was not originally included as a requirement in the design. It showed up later in response to the growing demand for supporting non-SOAP services over HTTP capable of exchanging plain XML, text, and JSON. So WCF got a facelift and the webHttpBinding infrastructure was added and later on the notorious REST starter kit. Whatever way you look at it, HTTP support from WCF was a bolted-on API.
While WCF, despite its over-configuration and annoying use of attributes, still does its job very well when it comes to supporting non-HTTP protocols or advanced capabilities such as message queuing and transactionality. It is clear that developers needed to stop thinking about HTTP as just one transportation protocol.
ASP.NET MVC was not originally created to provide a thinner layer to route HTTP requests to a backend. However, being characterized by a neat separation of concerns between logic and view, it turned out to be an effective way to replace WCF services. All developers need to do is having a controller class-preferably a distinct controller class-that just accepts GET requests and returns JSON or XML data instead of HTML. It worked, and still works, beautifully.
More recently, the .NET 4.5 Web API came to challenge most of the certainties about building an HTTP public API that developers strenuously built over the past couple of years. Before we get to Web API and find its exact role in the picture, let me take you through the canonical example that illustrates the need of a public HTTP API.
Suppose you have some data to expose to requesting clients; and suppose that clients can be of different types and require different formats. As an example, imagine that you need to expose the same data as JSON for iOS or Android callers and as XML for Flash applications. (By the way, this is exactly the scenario I face almost every day.)
Building a JSON Action
Any controller method in an ASP.NET MVC application is designed to return a rather generic type known as the result of executing an action-the ActionResult type. An ‘action result’ is a kind of command that the ASP.NET MVC framework will perform on the raw results generated by the action. Put another way, ASP.NET MVC acts as the mediator between the site backend where the action is performed and the requesting client. Raw data returned by the site backend are packaged into a format that once executed by the runtime produces results that suit the requesting client. In this way, a list of data transfer objects or domain objects can become a JSON string, an XML stream or, why not, a HTML page.
From the developer’s standpoint, here’s the code required to expose an action that serves JSON to clients:
1 2 3 4 5 |
public ActionResult Tournament(Int32 id) { var t = TournamentHelper.GetInfo(id); return Json(t, JsonRequestBehavior.AllowGet); } |
The variable t contains a data transfer object that will be serialized to the JSON format when the ASP.NET MVC runtime gets to process the ActionResult object. The method JSON is simply a helper method on the Controller class that is equivalent to:
1 |
new JsonResult() {Data = t} |
For the purpose of cleaner code, you can also decide to replace ActionResult in the method’s signature with the more specific JsonResult type. In the end, JsonResult derives from ActionResult.
The purpose of this method is overall clear: it gets some information and returns it serialized as JSON. In light of separation of concerns you likely put this method in a distinct controller class, often named ApiController. You do this to keep methods that return plain data separated from methods that return HTML markup. What if, instead, at some point the same data must be returned also in another format, say XML?
Adding a XML Action
Getting the same data in two different formats poses a tricky design issue. If you designed the HTTP API with the concept of a “resource” in mind, then you can’t add any new method without forcing your original design into something different. Central in the REST vision of the web, a resource is essentially a logical entity that you manipulate via basic HTTP commands. You can certainly GET a resource; but the GET action is unique. You can’t query for the same resource via two distinct GET commands.
To stay with the previous sample method, you should avoid having a pair of distinct actions like below.
1 2 3 4 5 6 7 8 9 |
public ActionResult TournamentJson(Int32 id) { var t = TournamentHelper.GetInfo(id); return Json(t, JsonRequestBehavior.AllowGet); } public ActionResult TournamentXml(Int32 id) { var t = TournamentHelper.GetInfo(id); return new YourXmlFormatter().Serialize(t); } |
A RESTful design is not the only option, though. You can decide that you are more of a RPC guy. In this case, from the design perspective it is acceptable to have multiple actions around the same data. It’s the client that establishes the actions required in the backend.
So the RPC route may calm down your design anxiety, but it is less than ideal to have two methods in the same controller performing the same action except for the serialization of the final data. I don’t even think that splitting the JSON and XML API into distinct controllers is really a better result. You could end up with the following:
1 2 3 4 5 6 7 8 9 10 11 12 |
public class JsonController { public ActionResult Tournament(Int32 id) { : } } public class XmlController { public ActionResult Tournament(Int32 id) { : } } |
In the end, the logic to execute is one and this makes for just one method to be in your public list of endpoints. The serialization format is a parameter and, as such, it should be passed in some way to the controller.
Merging Two Actions in One
So we are now leaning towards a situation in which there’s a single method that internally decides how to return its data.
1 2 3 4 |
public ActionResult Tournament(Int32 id, Boolean xml = false) { // Get raw data var t = TournamentHelper.GetInfo(id); |
1 2 3 4 5 |
// Serialize raw data if (xml) return new YourXmlFormatter().Serialize(t); return Json(t, JsonRequestBehavior.AllowGet); } |
The code snippet illustrates how you can add an extra (and optional) parameter to the method. The parameter has a default value that triggers the most common and default scenario you envision; in this case serializing via JSON. In terms of resulting URLs, differences are minimal:
1 2 |
http://yourserver/controller/tournament/123 http://yourserver/controller/tournament/123?xml=true |
The former URL returns JSON; the latter returns XML. More importantly, no redundant code is found anywhere.
This is a fairly standard approach and reasonably the best way of achieving this result up until ASP.NET MVC 3. ASP.NET MVC 4 and more specifically Web API provided out-of-the-box the ability to have content negotiation. You have no longer the need to process additional parameters and don’t even need any longer to return an ActionResult type out of the controller method. Your method signature is now even simpler because it just returns the raw data type. The new runtime machinery does the trick of figuring out the expected content type and instructs the internal serializer.
Using Web API
Packed with the .NET Framework 4.5 and well integrated with ASP.NET MVC and ASP.NET Web Forms, the Web API is basically yet another HTTP-related framework. Apparently it has a significant overlapping with ASP.NET MVC controllers. Compared to plain ASP.NET MVC controllers, Web API has a cleaner design and, more importantly, it is not bound to IIS and can be hosted nearly everywhere including in custom applications. Here’s how you start building a public HTTP API using the Web API framework:
1 2 3 4 5 6 7 8 9 |
public class NewsController : ApiController { : public IEnumerable<string> GetLatest() { var data = _service.GetNews(); return (from n in data select n).Take(5).ToList(); } } |
You derive your classes from ApiController-a new framework class-and add plain methods returning plain .NET types instead of ActionResult containers. Web API, however, still works on top of HTTP so somewhere there’s an entire HTTP pipeline that does things like binding the HTTP request parameters to method parameters and transforming return data into a HTTP response.
The underlying Web API machinery is fairly rich and powerful and it really makes the whole Web API thing look extremely simple. Hold on, though. The simplicity you perceive derives from use (if not abuse) of conventions. For example, every method that begins with Get is automatically associated to the GET verb. Does it sound great? It is, but it also means that you can’t have two methods whose name begin with Get in the same controller class. How does it sound now? Is it weird, isn’t it?
Web API pushes a vision of the world that it is REST based; according to this vision everything is a resource and can be acted on via HTTP verbs. In this regard, it is normal that you can’t have two GET methods. Oh well, you can actually have two GET methods in the same Web API controller named the way you like. To get this, you must use HttpGet attributes exactly as you would have done in plain ASP.NET MVC controllers.
1 2 3 4 5 6 7 8 9 10 |
public class NewsController : ApiController { : [HttpGet] public IEnumerable<string> Latest() { var data = _service.GetNews(); return (from n in data select n).Take(5).ToList(); } } |
How would you solve the XML/JSON dilemma in Web API? Web API does have a feature called content negotiation that solves the dilemma for you. In Web API, any values returned by methods are serialized via internal formatters. The default formatter uses JSON; however, the internal machinery is flexible enough to look at the header Accept and switch to XML if the text/xml content type is specified. In this way, you don’t need to change a single line of code in your Web API controller to serve JSON or XML to your clients. The following jQuery call will get JSON or XML depending on the Accept header:
1 2 3 4 5 6 7 8 9 |
$.ajax({ url: "/api/news/all", type: "GET", headers: { Accept: "text/xml; charset=utf-8" }, statusCode: { 200: function (listOfNews) { callback(listOfNews); }, 404: function () { alert("No news Found!"); } } }); |
Is this great news? It may or may not.
Conventions when not well explained, understood, and known are quite dangerous. They do not favor readable code and even may create more issues than they would solve in development scenarios where awareness of conventions is not high. Too many, and too deep conventions, look undistinguishable from magic. Personally, I find acceptable the level of conventions you find in ASP.NET MVC; much less the extreme level of conventions you find in Web API.
Regardless of personal feelings, though, Web API does have built-in content negotiation over classic ASP.NET MVC. However, for the core problem of returning both XML and JSON you should also consider the impact of the XML schema. The default XML formatter replicates the structure of the class you’re attempting to serialize. This mostly works most of the times. If not, you must write your XML formatter and, worse yet, plug it into the machinery. The resulting code is much less immediate to write and understand than a plain IF branch and a parameter as you would do outside Web API.
It is good to have Web API, but for the moment I’d say that if you can achieve your goals without it, or without seeing a clear benefit in using it, then you should just skip over it and stick to old good MVC controllers. For what that matters, I’m not using Web API anywhere and all of my public HTTP APIs are based on MVC controllers.
Load comments