Wednesday 29 October 2014

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.
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. SinceASP.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 aLocation header to our response message, indicating what the location is of the newly created product. You will need to reference thesystem.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 tosystem.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 theOnWriteToStreamAsync 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 ourglobal.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 partyJson.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 theSystem.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.

1 comment:

  1. I like your blog, I read this blog please update more content on hacking,
    Nice post,and good information Thanks for sharing
    further check it once at Dot NET Online Course Hyderabad

    ReplyDelete

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...