The ASP.NET Web API has been released with the ASP.NET MVC4 beta release, which was released 2 days ago (14/02/2012).
You can download the ASP.NET MVC4 beta release, that includes the Web API, here:
Quoted directly from microsoft website:
ASP.NET MVC 4 also includes ASP.NET Web API, a framework for building and consuming HTTP services that can reach a broad range of clients including browsers, phones, and tablets. ASP.NET Web API is great for building services that follow the REST architectural style, plus it supports RPC patterns.
If you do not know what REST stands for and why it could be of any use, you can watch this 1h18m08s video on channel9 by Aaron Skonnard: http://channel9.msdn.com/Blogs/matthijs/Why-REST-by-Aaron-Skonnard
Considering how popular REST is these days, it might be interesting to cover the new Web API in this post. Originally we saw REST services coming up through the WCF pipeline, like the WCF Data Services or using a common WCF service with the WebHttpBinding, which works on HTTP verbs like GET, POST, PUT and DELETE. However WCF was created as a messaging platform, on which we are working with SOAP messages. The entire WCF pipeline is also optimized for messaging. REST services do work a bit differently, nor do they use any SOAP. Apparently Microsoft came to the conclusion that the integration of REST was not ideal with the WCF messaging pipeline so they moved the possibility to create REST services within the ASP.NET Platform.
I already wrote some posts on this blog before about REST services:
WCF REST service with XML / JSON response format according to Content-Type header
WCF REST service operation with JSON and XML also supporting ATOM syndication feed format
WCF REST service with ODATA and Entity Framework with client context, custom operations and operation interceptors
It might be useful to browse once quickly through these posts, so you understand what REST is, how it works and how content negotation works,and how REST currently is implemented by WCF. I will not cover the HTTP verbs nor will I go into detail into content negotation. You can find these basics in the previous posts I’ve written. I will simply cover the ASP.NET Web API basics.
1. Creating a ASP.NET Web API service
After you installed the ASP.NET MVC4 Beta on your computer, create a new ASP.NET MVC4 Web Application:
On the next screen, you will see some extra templates are available in comparison to ASP.NET MVC3:
In our case, we will create a Web API project. After creation your project will look like a common ASP.NET MVC project.
We will expose a REST service that will expose some data to our application, clients or mobile users for example.
Our solution will look like this:
To have some data to work with, I’ve added an ADO.NET Entity Data Model on the AdventureWorks database. If you do not know what Entity Framework is or how to work with it, you might want to browse to some topics I’ve writting on Entity Framework: http://robbincremers.me/category/entity-framework/
We also added a folder to our solution called “Api” in which we will place the controllers which expose data. By default under your Controllers folder you will have a file called ValuesController, which is the default template for a Web Api Controller. In our case, we simply remove the file and we will create our own Web API Controller under the API folder.
In our AdventureWorks Entity Data Model we exposed 1 class Product from our AdventureWorks database:
Right-click the Api folder, under which all our Web API controllers will be placed, and add new item and we will add a MVC4 Controller Class, called “ProductsController”:
By default, our new controller will inherit from Controller:
To work with a Web API controller, your controller will need to derive from the ApiController:
The new ApiController and related items for the ASP.NET Web API can be found under the System.Web.Http namespace.
REST services work based on the HTTP verbs like GET, POST etc. The new ASP.NET Web API is convention based:
We want to expose an operation of the Products that is a GET Operation, which will expose the entire list of products to our clients. Since ASP.NET Web API is convention based, the name of the method has to match the HTTP verb you want to invoke or have a method name that starts with the HTTP verb, like for example GetProduct would work aswell. In our case we want to expose a GET operation on the /products/ uri. Invoking this GET operation from the browser:
Notice if we browse to the /api/products/ location we directly invoke the GET operation which returns a list of all products present in our database. The /api/ prefix in our uri is defined in the global.asax. By default the template will add the /api/ for the exposed web api services:
You can change this to anything you like. Changing mappings for parameters or exposed services can be done through the routing that is possible in ASP.NET. We now exposed an IEnumerable<Product> on our GET operation, however the ASP.NET Web API also supports exposing IQueryable<T> on your operations:
One of the nice things through exposing an IQueryable is that the client can construct queries with Odata operations and that only the custom query will be invoked on the database, instead of retrieving all products from the database and only applying the filter on the yet retrieved products. However this are basics from the Entity Framework, which I will not cover now. If you do not know what IQueryable is or what lazy loading and deferred loading is, I suggest reading through some of my Entity Framework posts.
The ASP.NET Web API also supports most of the OData protocol:
You can use OData operations as $skip, $top, $filter etc. If you do not know about OData, you can find the basics in this post:
WCF REST service with ODATA and Entity Framework with client context, custom operations and operation interceptors
To add a GET operation for a single product, that accepts an id parameter:
Note you can also pass along other parameters, you just need to make sure your routing at the global.asax is configured to pass along those parameters. Retrieving a single products through the id:
ASP.NET Web API also allows you to use content negotiation to retrieve the format of the data. Through the Accept header you can request for a certain type of data format. If the REST service supports this format, you will get the data in this format back. If it does not, it will return the data in the default format.
With Fiddler we will request the products with id 999 with a GET operation and pass along the Accept header as “application/xml”. So we are requesting some data and tell the browser that we accept application/xml as content-type. In case the service supports this format, we will get the data back as XML:
The result will be that the data will be returned as XML:
If we do another GET operation for a specific product and pass the Accept header along as “application/json”, we will get JSON returned:
Creating the other operations can be done by using the Post, Put and Delete operations to the controller:
Invoking the POST operation from the Fiddler client and passing along a new product as XML (Notice we use the POST HTTP verb in Fiddler):
After invoking the operation, the new product will be added in our database:
You could also send products to the service as JSON with the Content-Type header set as “application/json”.
Deleting a products though our Web API service:
Invoking this operation through Fiddler:
After invocation (Notice the DELETE verb we use in Fiddler), the record is removed again from the database:
However when building REST services, we should be using the correct HTTP response codes on our operations. If someone invokes the POST operation, an HTTP statuscode of 200 will be returned to the client, which means the operation was successful:
However the correct HTTP statuscode for creation is the HTTP statuscode 201, which stands for created. We can adjust our Post operation so that our operation will return the correct HTTP Statuscode to the client, which indicates the object was created:
We use an HttpResponseMessage as return parameter, in which we set the Statuscode to HttpStatusCode.Created. We also attach a Location header to our response message, indicating what the location is of the newly created product. You will need to reference the system.net and system.net.http namespaces. If you create a new product by the POST operation:
We get the correct HTTP statuscode 201 returned, instead of the default statuscode 200. We also specified a Location header where the new product can be found, which gets returned to the client.
2. Using MediaTypeFormatter to provide content types other then XML or JSON
In the AdventureWorks database, each product has a related class ProductPhoto in which a thumbnail and large photo are binary saved in the database. If we want to expose a ProductsPhotos controller which exposes the information of the product photos, we create an ApiController called ProductPhotos:
We import the ProductPhoto from our AdventureWorks database into our ADO.NET Entity Data Model:
Notice we have a property called LargePhoto which contains the image in binary format in the database. We create a new ApiController for exposing the ProductPhotos:
We add some basic code to our new ApiController:
If you now get the ProductPhoto information for the ProductPhoto with id 69, we will get the information provided in XML:
This is nice in case we want to retrieve all the ProductPhoto information. However in some cases we will want to retrieve to image itself and not the binary format of the image that gets written out by default. By default Accept types as “application/xml” or “application/json” are supported. However we want to set the Accept header as “image/jpg” for example and when we use that Accept header, we want the image to be returned.
Doing this can be achieved by creating a class that derives from the MediaTypeFormatter class, which belongs to system.net.http.formatting namespace.
We create a new MediaTypeFormatter that will write the image of the ProductPhoto to the client if they invoke the GET operation of the ProductPhoto with an Accept header of “image/jpg”. Our MediaTypeFormatter will look like this:
The code of our custom MediaTypeFormatter:
In the constructor of our custom MediaTypeFormatter we add a MediaTypeHeaderValue of “image/jpeg” to the SupportedMediaTypes. We also define that this custom media type can only be written if the object type is ProductPhoto. Finally we create a task in the OnWriteToStreamAsync method that will write the binary content of the ProductPhoto.LargePhoto to the stream that is being passed along.
To have this custom MediaTypeFormatter being added in our request/response pipeline, we have to register it. We do this in our global.asax by the GlobalConfiguration.Configuration.Formatters collection:
If we with Fiddler invoke a GET request on the /api/productphotos/69/ we will get the following result:
So depending on our Accept header, we can get the ProductPhoto information back in XML or JSON, but we can also get the ProductPhoto image returned if we add a custom header through the MediaTypeFormatter.
Now if for example you don’t want to expose the ProductPhoto information and you only want to expose the image directly on the Product if they ask for a Product with an Accept header of “image/jpeg”. In that case you would write your MediaTypeFormatter for the “image/jpeg” Accept header to take a Product object. On the writing of the stream you will do a database query to get the related ProductPhoto and write the binary content to the stream. That way you would not have to expose another ApiController for the only reason to expose an image for a Product.
Do get the ProductPhoto directly from the Product GET operation instead of using a ProductsPhotosController, you can rewrite the MediaTypeFormatter like this:
We change the CanWriteType operation to only return true when the type is of Product. That means if you request information that is not of type Product, the “image/jpg” formatter will not work and the default format will be returned, which is JSON or XML, depending on the browser you use.
We remove the ProductsPhotosController, since we don’t need it anymore. We are not interested in the ProductPhoto information, we only want to return an image that is related to a product, so we can do that directly on the Product GET Operation by an image/jpg Accept header.
Our GET operation for a specific product still contains the same code:
If we request for the Product with ID 332 with an Accept header of image/jpg we get an image back:
If we now invoke the same uri with an JSON accept header:
With content negotation and custom MediaTypeFormatters we can write support for quite some content types, with one and the same operation.
3. Using the JSON.NET serializer instead of the default DataContractJsonSerializer
As someone commented, if you use Entity Framework 4.0 your entity classes will be generated with the [DataContract(IsReference=true)], which is being used for circular references. This is because the EntityObject, which our entity classes derive from, have this IsReference=true attribute set.
However the default DataContractJsonSerializer does not support references, so it is not able to serialize your entity class to a JSON result. You might get an error like this:
The type ‘type’ cannot be serialized to JSON because its IsReference setting is ‘True’. The JSON format does not support references because there is no standardized format for representing references. To enable serialization, disable the IsReference setting on the type or an appropriate parent class of the type.
To solve this issue, you can use the Json.NET serializer instead of the default DataContractJsonSerializer. You can find the third party Json.NET serializer here: http://json.codeplex.com
If you look at the features of Json.NET, you’ll see it supports circular references, whereas the default DataContractJsonSerializer does not.
How to implement the Json.NET serializer in ASP.NET Web Api:
This issue will be avoided if you use Entity Framework 4.2 Code First with DbContext since you can manage your entity classes yourself.
4. Using the HttpClient to request and serialize data from Web Api
Providing the data is one thing, consuming the data is another. One of the tools you can use to retrieve and post data to your Web Api service is the HttpClient, which belongs to the System.Net.Http namespace.
Suppose I have an Order class, which looks like this:
I have an Api OrdersController which exposes a GET operation for an IQuerable<Order>:
The reason why we expose the data as an IQueryable is so that we can execute OData filters on it. We have an normal OrdersController which will list the orders through a scaffolding list view. We have an operation to Deserialize an JsonArray to a list of object:
We create a new HttpClient with an Accept header of “application/json”, which means we want to retrieve the orders data in JSON format. You can set the headers on the DefaultRequestHeaders property. Finally we have some async task clutter in there to get the result of the response as a JsonArray. Finally we invoke our operation to return any JsonArray to an List<T>, which will return a list of orders. The code I wrote for running the tasks might not be idea as I seriously need to look a bit more into the task threading.
We added some code to get the data on the /api/orders?$top=5, to get the top 5 orders from our orders api service. The $top is an OData operator, which will only work when you exposed the data as an IQueryable. We get the content from our response and read it as an JsonArray, which we will serialize back to our object.
Visiting the index view for the orders controller, we only get 5 results back:
Setting some other OData filter on our request, we will use the OData $filter operator to filter all Orders that have a customer with the value of my name
And the results:
Through the OData protocol we can apply selecting certain rows with the $skip and $top operations for paging, but we can also do some very strong filtering with the $filter. On our Web Api service only the query is being executed on the database with the filter, so it does not retrieve all information first and then apply the filter.
You could also write the previous code like this:
The HttpClient also exposes some other functions which are need to manage your requests:
- GetStreamAsync: Send a GET request to the specified Uri and return the response body as a stream in an asynchronous operation.
- GetStringAsync: Send a GET request to the specified Uri and return the response body as a string in an asynchronous operation.
- GetByteArrayAsync: Send a GET request to the specified Uri and return the response body as a byte array in an asynchronous operation.
- PostAsync: Send a POST request to the specified Uri as an asynchronous operation.
- PutAsync: Send a PUT request to the specified Uri as an asynchronous operation.
- DeleteAsync: Send a DELETE request to the specified Uri as an asynchronous operation.
This is still the beta and if I’m correct, the ASP.NET team should have added some features for managing different content types, but guess it’ll be waiting till the release. I seriously hope they bring an improved HttpClient that abstracts all the clutter for us and allows use to easily call request and serialize them to objects depending on a content-type of the reponse that comes back.
With the result:
Using OData operations is as easy:
Looking for more information of ASP.NET Web API. You can find an overview of interesting ASP.NET Web API posts here:
The msdn blog of Henrik F. Nielsen, responsible for Web API, which is covering more advanced topics with ASP.NET Web API:
Any suggestions, remarks or improvements are always welcome.
If you found this information useful, make sure to support me by leaving a comment.
Cheers and have fun,