Friday, April 20, 2012

SData URLs in MVC .NET Web API

In this post I'm going to explain how to build RESTful Web Services with ASP .NET Web API and support SData:
  • Single Resource and Collection URLs (via Web API Routes)
  • Queries  (E.G: where, orderby) 
  • Paging  (E.G: startIndex, count)
  • Payloads
I mentioned in a previous post that currently the SIF is the de facto standard for building SData web services in .NET. The SIF is a very good framework for building SData web services. I have participated in three projects where we used the SIF to provide SData. The SIF is a good foundation for building SData web services, but there're other options you might want to explore too.

RESTful Web Services With ASP .NET Web API


One of the projects I'm working on right now is a Mobile web application that we're building with Microsoft's ASP .NET MVC framework. I really like ASP .NET MVC; I like:
  • Model, view, controller pattern is implemented well in the framework, the clear separation of concerns
  • Tool support with Visual Studio is excellent
  • Documentation and tutorials are abundant
  • Open source under Apache 2.0 license

I'll write more about building web pages with ASP .NET MVC in the future, but today I want to discuss building SData web/HTTP services with ASP .NET MVC 4 Web API.

Building webservices and/or pages with ASP .NET MVC is really easy. There're a large number of tutorials available from Microsoft and others. For example this Microsoft tutorial will walk you through creating a simple  REST web service in Visual Studio with MVC 4 Web API in just four minutes. With just a couple of mouse clicks you can have a simple REST web service that returns either XML or JSON content. 

I also provide complete instructions on how to generate a RESTful web service with Visual Studio 11 and MVC 4 Web API. See my blog SData with Entity Framework  (EF) Model & Database First

Supporting SData URLs

It's really easy to add support for SData URLs to a Web API service. Follow the Microsoft tutorial or my tutorial mentioned above so you understand the basics of creating a Web API project, and so you have simple project in Visual Studio and then just do the following to add support for SData Single resource and Collection URLs:

Web API SData Routes


The Web API new Project Wizard creates Global.asax.cs file with standard MVC routes and Web API routes. Open the Global.asax.cs file. The standard Web API route is something like:

            routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );


1. Add a route for SData Single Resouce URL
E.G: http://www.example.com/sdata/accounts('A001')

            routes.MapHttpRoute( name: "SDataSingleResourceKind",
                routeTemplate: "sdata/{controller}({selector})",
                defaults: new { selector = RouteParameter.Optional });


The route above tells a Web API message handler that when a user agent requests/GETs a single resource called "accounts" with the ID "A011", find the controller called "AccountsController", and then find the "Get" method within the controller that accepts one argument called "selector" and pass "A001" as the value for that argument. E.G. The "AccountsController" requires roughly the following method to handle this request and return the account with the specified ID:

        // GET http://www.example.com/sdata/accounts('A001')
        public Account Get(string selector)        {
            return db.Accounts.Single(o => o.AccountID == selector);
        }

2. Add a route for SData Resouce Collection URL
E.G: http://www.example.com/sdata/accounts


            routes.MapHttpRoute(  name: "SDataCollection",
                routeTemplate: "sdata/{controller}/{query}",
                defaults: new { query = RouteParameter.Optional }


Basically route 2 above tells a Web API message handler that when a user agent requests/GETs a collection of resources (an SData feed) called "accounts" find the controller called "AccountsController", and then find the "Get" method within the controller that requires NO arguments. E.G. The "AccountsController" requires roughly the following method to handle this request:

// GET http://www.example.com/sdata/accounts
public IQueryable<Customer> Get() {
return db.Customers.AsQueryable();
}
At this point you should have a basic Web API web service that can handle a request for an SData single resource really well and simple SData Collection request like:
http://www.example.com/sdata/accounts

SData Queries


However, SData includes a very complete query language which is NOT supported by the Web API routing. In SData you can do far more then just request a collection of resources; you can filter, page, sort, etc. The following URL:
sdata/salesOrders?where=subTotal gt 1500.0 &orderBy=orderDate &startIndex=21&count=10
Requires the service to response with all of the SalesOrders:
  • Where the subTotal is greater then $1500
  • Ordered by the date
  • Starting with the twenty first resource
  • Including a maximum of ten resources (results 21 - 31)
Adding support for the SData query language would require complex controllers that included multiple overloaded GET methods which accepted a different number of method arguments. The methods would require logic that parsed the where, orderby, etc. and executed LINQ/query's.

Fortunately, there's a simple shortcut. If your resource collection method (the NO argument GET) returns IQueryable like the example above then you simply have to reference our Sage.SDataHandler Library in your Web API project. The SDataHandler class library is a POC I developed to illustrate how easy it is to add SData support to MVC .NET Web API projects. The SDataHandler enables even complex SData queries.

The source code and a build of Sage.SDataHandler is available on GitHub. Download the Sage.SDataHandler.dll from GitHub then just add a reference to the Sage.SDataCommon in your Web API project and add the following code to your project's Global.asax.cs:
using Sage.SDataHandler;
In the "Application_Start()" method somewhere after the Registering the routes add the following one line of code:
GlobalConfiguration.Configuration.MessageHandlers.Add(new SDataHandler());
The "Application_Start()" method should look like:
protected void Application_Start()
{
....
        RegisterRoutes(RouteTable.Routes);
        GlobalConfiguration.Configuration.MessageHandlers.Add
new SDataHandler());
...
}
Now run your Web API service, open it in a web browser and try some complex SData URLs. The  SDataHandler will handle the complex SData queries. I'll explain how the   SDataHandler class works in a future post.

SData Payloads


To create a complete SData provider you still have to generate and return SData compliant payloads; the service must respond with valid SData XML (based on ATOM) or JSON. The SDataHandler class will provide JSON payloads which will be compliant with SData 2.0.




No comments:

Post a Comment