Building and consuming REST services with ASP.NET Web API using MediaTypeFormatter and OData support

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:
http://www.microsoft.com/download/en/details.aspx?id=28942

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:

ASP.NET Web API REST services

On the next screen, you will see some extra templates are available in comparison to ASP.NET MVC3:

ASP.NET Web API REST

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:

ASP.NET Web API REST

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:

WCF Web API REST

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”:

ASP.NET Web APi building and consuming REST services

By default, our new controller will inherit from Controller:

ASP.NET Web Api REST

To work with a Web API controller, your controller will need to derive from the ApiController:

ASP.NET REST Web API with 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:

ASP.NET REST Web API

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:

ASP.NET Web API REST

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:

REST GET POST PUT DELETE

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:

ASP.NET Web API for REST services

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:

ASP.NET Web API REST ODATA

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:

ASP.NET Web API REST

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 REST ODATA

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:

ASP.NET Web API REST ODATA JSON XML

The result will be that the data will be returned as XML:

REST with ODATA and Json, 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:

ASP.NET Web Api REST ODATA JSON

Creating the other operations can be done by using the Post, Put and Delete operations to the controller:

ASP.NET Web API REST POST

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):

ASP.NET Web Api REST ODATA

After invoking the operation, the new product will be added in our database:

ASP.NET Web Api REST ODATA

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:

ASP.NET Web Api ODATA REST

Invoking this operation through Fiddler:

ASP.NET Web API REST

After invocation (Notice the DELETE verb we use in Fiddler), the record is removed again from the database:

ASP.NET Web Api REST ODATA
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:

ASP.NET MVC4 Web API REST ODATA

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:

ASP.NET MVC4 Web API REST ODATA

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:

ASP.NET MVC4 Web API REST ODATA

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:

ASP.NET MVC4 Web API REST ODATA

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:

ASP.NET MVC4 Web API REST ODATA JSON XML

We add some basic code to our new ApiController:

ASP.NET MVC4 Web API REST ODATA JSON XML

If you now get the ProductPhoto information for the ProductPhoto with id 69, we will get the information provided in XML:

ASP.NET MVC4 Web API REST ODATA JSON 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:

ASP.NET MVC4 Web API REST ODATA JSON XML

The code of  our custom MediaTypeFormatter:

ASP.NET MVC4 Web API REST ODATA JSON XML

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:

ASP.NET MVC4 Web API REST ODATA JSON XML

If we with Fiddler invoke a GET request on the /api/productphotos/69/ we will get the following result:

ASP.NET MVC4 Web API REST ODATA JSON XML

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:

ASP.NET MVC4 Web API REST ODATA JSON XML

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.

ASP.NET MVC4 Web API REST MediaTypeFormatter image

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.

ASP.NET MVC4 Web API REST MediaTypeFormatter image

Our GET operation for a specific product still contains the same code:

ASP.NET MVC4 Web API REST ODATA JSON XML

If we request for the Product with ID 332 with an Accept header of image/jpg we get an image back:

ASP.NET MVC4 Web API REST MediaTypeFormatter image

If we now invoke the same uri with an JSON accept header:

ASP.NET MVC4 Web API REST ODATA JSON XML

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:
http://blogs.msdn.com/b/henrikn/archive/2012/02/18/using-json-net-with-asp-net-web-api.aspx

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:

ASP.NET Web Api consuming api service with HttpClient

I have an Api OrdersController which exposes a GET operation for an IQuerable<Order>:

ASP.NET Web Api consuming api service with HttpClient

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:

ASP.NET Web Api consuming api service with HttpClient

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.

ASP.NET Web Api consuming api service with HttpClient

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:

ASP.NET Web Api consuming api service with HttpClient

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

ASP.NET Web Api consuming api service with HttpClient

And the results:

ASP.NET Web Api consuming api service with HttpClient

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:

ASP.NET Web API HttpClient with JSON

It’s more compact code and works in a synchronous manner. We use the JavaScriptSerializer in this case, which belongs to the System.Web.Script.Serialization namespace. You’ll need to import the System.Web.Extensions.dll to have access to this namespace. In many cases using the full asynchronous code is better, but at the end of our code, we need to return the list of objects to our view, so it has to be loaded before we pass it along, so you might just as well write it as synchronous code in this case.

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.

You could also retrieve the json information from your web api service through a javascript ajax call, instead of using the HttpClient:

ASP.NET Web Api json information

With the result:

ASP.NET Web API JSON

Using OData operations is as easy:

ASP.NET Web API

The result:

ASP.NET Web Api consuming json with ajax

Looking for more information of ASP.NET Web API. You can find an overview of interesting ASP.NET Web API posts here:
http://www.tugberkugurlu.com/archive/getting-started-with-asp-net-web-api-tutorials-videos-samples

The msdn blog of Henrik F. Nielsen, responsible for Web API, which is covering more advanced topics with ASP.NET Web API:
http://blogs.msdn.com/b/henrikn/

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,

Robbin

24 comments on “Building and consuming REST services with ASP.NET Web API using MediaTypeFormatter and OData support

  1. Great post!
    I have some problems with getting data in json format however. I’m trying it with my own database and while sending POST request with Accept: Application/json, I get the following error:
    “The type user 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.

    Do you know how could I solve it?

    • This is because of the [DataContractAttribute(IsReference=true)], which is used for circular references, which is being added to your default generated entity objects with using Entity Framework 4.0. JSON does not support references, so it can not serialize the content to JSON because it does not know how to. The issue is that the EntityObject, which our entity classes derive from, have the IsReference=true set on their DataContractAttribute.

      Solution:
      Change the JSON serialization from the default DataContractJsonSerializer with the third-party JSON.NET serializer (http://json.codeplex.com/). If you look at the homepage if the JSON.NET serializer, you’ll see it supports circular references

      How to integrate the JSON.NET serializer in ASP.NET Web Api:
      http://blogs.msdn.com/b/henrikn/archive/2012/02/18/using-json-net-with-asp-net-web-api.aspx

      PS: When doing a POST request you should use Content-Type header to define what the type of the content is that you are submitting. The accept header is to define what sorts of content types you can accept, but it does not say what type the content is that you are submitting.

  2. Moreover, I have also problem with sending POST requests.
    I get the following error:
    “{“ExceptionType”:”System.InvalidOperationException”,”Message”:”No ‘MediaTypeFormatter’ is available to read an object of type ‘User’ with the media type ”undefined”.”,”StackTrace”:” at System.Net.Http.ObjectContent.SelectAndValidateReadFormatter(Boolean acceptNullFormatter)\u000d\u000a at System.Net.Http.ObjectContent.ReadAsyncInternal[T](Boolean allowDefaultIfNoFormatter)\u000d\u000a at System.Web.Http.ModelBinding.RequestContentReader.ReadWholeBodyAsync(HttpActionContext actionContext, Type modelType)\u000d\u000a at System.Web.Http.ModelBinding.RequestContentReader.ReadContentAsync(HttpActionContext actionContext)\u000d\u000a at System.Web.Http.ModelBinding.DefaultActionValueBinder.BindValuesAsync(HttpActionContext actionContext, CancellationToken cancellationToken)\u000d\u000a at System.Web.Http.ApiController.c__DisplayClass3.b__0()\u000d\u000a at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)\u000d\u000a at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal(HttpRequestMessage request, CancellationToken cancellationToken)\u000d\u000a at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)”}”

    Hope you are able to give me some hints:)
    Thanks!

    • The same issue as the reply I gave on your previous comment. You need to pass the Content-Type header as “application/xml” or “application/json” when you are executing a POST request. If you do not pass the Content-Type HTTP header it does not know what the content type is of the data you submitted, so it takes a content-type of “undefined”.

      Then it checks if there is a MediaTypeFormatter for a content-type of “undefined”, which it does not find. That’s the exception you are getting back.

  3. Hi, I have been looking everywhere but cannot find the answer to this question. I am developing a RESTful API using WebAPI and want to do the following. Suppose you have a list of movies and each movie has a list of actors each with their own properties (eg.: age, sex, birthday, etc.). I would like to present the response of that subset of actors with URI’s inside every movie as follows:

    The Artist
    2011

    api/actors/1
    api/actors/2

    Is there anyway to automatically do the uri thing inside the “actor” tag using WebAPI?

  4. Correction: the xml tags were removed from my previous post but the “actor” tag is the one that contains the “api/actors/1″ content. That URI is the one I want to be automatically generated.

  5. Pingback: What is RESful service and designning RESTful service using ASP.NET | Broken Code

  6. Hi Robbin. Don’t suppose you have the bit of js / jquery you would use to display the image have you? It’s eluding me at the moment. Great article.

  7. Hi, is there anyway to reduce the amount of content the Json string returns, as Im getting extra entity info in addition to my normal data here’s an example of the extra data :
    “EntityKey”:{“$id”:”4″,”EntitySetName”:”Users”,”EntityContainerName”:”GustareEntities”,”EntityKeyValues”:[{"Key":"UserID","Type":"System.Int32","Value":"900990"}]}}]
    Thanks!

  8. Hi there,

    Great post, have my image serving up nicely in fiddler.
    However I now have a silly question, how can i display this image on a webpage in html?

    • U know what I figured it out,,

      <img src="data:image/jpg;base64,/9j/4AAQSkZJ…….

      so looks like in my case i didn't need the content negotiation after all, nice post still the same.

  9. Pingback: Interesting post on ASP.NET WebAPI « Synthesis – The combination of ideas into a complex whole

  10. 2 Next, you will hqve the freedom to choose limited liability your words the right way to promote your
    company without going to advertising agencies,
    newspapers, and magazines. Handle your business Recruit 10
    people, consider the services offered within thee Visa card community.

    It does take a lot of copy. The value for business les in its history aas a limited liability center for transportation and consumer retail-
    yieding very large entities. It’s where you’ve become complacent, satisfied with what you
    believe in and your efforts will be rewarded.

  11. Thank you for the auspicious writeup. It in fact was a amusement account it.
    Look advanced to far added agreeable from you! However, how could we communicate?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s