ASP.NET Web API vs OData
With the 1.2.6 release, JayData provides an ASP.NET Web API specific data provider to let you connect not only to OData enabled endpoints but simple ASP.NET Web API backed REST services too. Out of the box the ASP.NET Web API supports a truly REST approach for dealing with data and borrows some concepts from OData standard mainly on the query aspect of data. It’s important to note that ASP.NET Web API by default is not OData! It’s a much simpler thing for a list of simple use cases.
Our philosophy regarding our approach to support ASP.NET Web API was that the default controller templates, generated by the Visual Studio “Add controller” wizard should work WITHOUT any further ado. No additional server components and dll, no custom controller filters, no custom metadata services. This though keeping our server code simple definitely limits the capabilities of our endpoint on a way not all projects can accept. Read the list of limitations here. For those finding the basic Web API features insufficient the ultimate solution is to extend your project to support standard OData. This became increasingly easy and also powerful with the Microsoft ASP.NET Fall Update preview that arrived recently. An upcoming post will show you how to upgrade your simple Web API project into an OData V3 compliant solution.
Create the Todos Controller
Let’s start off with an ASP.NET Web API project with a simple Todo database in it. We use Entity Framework and SQL CE 4.0 to define a simple Todo entity and also to build up the database. Read this post for a step-by-step guide on creating the Todo test project.
Now right click on the Controllers folder and select Add Controller. Set “TodosController” as the controller name. Select the “API controller with read/write actions, using Entity Framework” as the scaffold template. For Model class select Todo and TodoModelContainer for data context class.
To test your controller launch your project with F5 and navigate to http://yoursite/api/todos . Depending on your Web API version you should see an XML feed or a JSON document with your Todo items listed.
Use the JayData ItemStore API with the webApiProvider
We will test drive our Web API based REST service from a single HTML5 page. In a real world scenario you would build these into MVC Views – but for the sake of simplicity let’s just have this code in a simple html file. So just add an html page to your MVC project, and call it application.html
Setup client
Scripts to include
1 2 3 4 5 6 7 8 |
<span class="kwrd"><</span><span class="html">html</span> <span class="attr">xmlns</span><span class="kwrd">="http://www.w3.org/1999/xhtml"</span><span class="kwrd">></span> <span class="kwrd"><</span><span class="html">head</span><span class="kwrd">></span> <span class="kwrd"><</span><span class="html">script</span> <span class="attr">src</span><span class="kwrd">="Scripts/jquery-1.8.3.js"</span><span class="kwrd">></</span><span class="html">script</span><span class="kwrd">></span> <script src=<span class="str">"http://include.jaydata.org/jaydata.js"</span>></script> <script> <span class="rem">//use $data here!</span> <span class="kwrd"></</span><span class="html">script</span><span class="kwrd">></span> |
Define the Todo type on the client
The ASP.NET Web API provider does not use server generated metadata to setup the client. You need to provide the JayData library with some information about the data types your about to deal with. This at the moment you can only do manually. Version 1.2.7 of JayData brings Visual Studio development time support for autogenerating the client environment.
1 2 3 4 5 6 7 8 9 10 |
<span class="kwrd">var</span> Todo = $data.define(<span class="str">"Todo"</span>, { Task: String, DueDate: Date, Completed: Boolean }); Todo.setStore(<span class="str">'default'</span>, { provider: <span class="str">'webApi'</span>, dataSource: <span class="str">'/api/<TodoControllerName>'</span> }); |
GET /api/Todos
List all Todo items.
1 2 3 4 5 6 7 8 |
$(<span class="kwrd">function</span> () { <strong><span style="font-size: medium;">Todo.readAll()</span></strong>.then(<span class="kwrd">function</span> (items) { items.forEach(<span class="kwrd">function</span> (item) { console.log(JSON.stringify(item)); }); }); }); |
Press F12 on most browsers to open up your console where the output is placed.
The result:
GET /api/Todos with filtering
To support server side filtering/querying for items, you have to modify your Controller code a bit.
Server code before:
1 2 3 4 5 |
<span class="rem">// GET api/Todos</span> <span class="kwrd">public</span> IEnumerable<Todo> GetTodoes() { <span class="kwrd">return</span> db.Todoes.AsEnumerable(); } |
Server code After:
1 2 3 4 5 |
<span class="rem">// GET api/Todos</span> <span class="kwrd">public</span> IQueryable<Todo> GetTodoes() { <span class="kwrd">return</span> db.Todoes; } |
Client code:
1 2 3 4 5 6 |
<strong><span style="font-size: medium;">Todo.query</span></strong>(<span class="str">"it.Completed == true || it.DueDate.year == 2012"</span>) .then(<span class="kwrd">function</span> (items) { items.forEach(<span class="kwrd">function</span> (item) { console.log(JSON.stringify(item)); }); }); |
GET /api/Todos/id
Todo.read
1 2 3 4 5 6 7 8 |
(1) .then(<span class="kwrd">function</span> (todo) { console.log(JSON.stringify(todo)); }) .fail(<span class="kwrd">function</span> (err) { console.log(<span class="str">"item not found"</span>); }); |
POST /api/Todos
To create a new item, use the save() method.
1 2 3 4 5 6 |
<strong><span style="font-size: medium;">Todo.save</span></strong>({ Task: <span class="str">"Build an HTML5 Application"</span>, DueDate: <span class="kwrd">new</span> Date(<span class="str">"2013-02-01"</span>) }).then(<span class="kwrd">function</span> (todo) { console.log(todo.Id); }); |
PUT /api/Todos/id
To update items use the save() method on an instance.
1 2 3 |
Todo.read(1) .then(<span class="kwrd">function</span> (todo) { |
todo.Completed = true; return todo.save();
1 2 3 4 |
}) .then(<span class="kwrd">function</span> (todo) { console.log(<span class="str">"item updated"</span>); }); |
DELETE /api/Todos/id
1 2 3 4 |
<strong><span style="font-size: medium;">Todo.remove(4)</span></strong> .then(<span class="kwrd">function</span> (todo) { console.log(<span class="str">"item deleted"</span>); }); |
Alternatively:
1 2 3 4 5 6 7 |
Todo.read(5) .then(<span class="kwrd">function</span> (todo) { <strong><span style="font-size: medium;"><span class="kwrd">return</span> todo.remove();</span></strong> }) .then(<span class="kwrd">function</span> (todo) { console.log(<span class="str">"item deleted"</span>); }); |