ASP.NET MVC 4 WebApi: Manually Handle OData Requests

I have a web service created using the WebAPI provided by ASP.NET MVC 4. I know that the layer that the WebAPI runs on top of it handles OData Queries automatically (like $filter , $top , $skip ), but what, if I want to process the filtering myself?

I am not just returning data from my database , but I have another layer that adds some properties, does some transformations, etc. So the query All my data, converting it and returning it to the WebAPI class to filter OData, is not only good enough. This, of course, is terribly slow, and generally a crappy idea.

So, is there a way to distribute OData strong> query parameters from my WebAPI entry point to the functions that I call to receive and transform the data?

For example, GET on /api/people?$skip=10&$top=10 will call on the server:

 public IQueryable<Person> get() { return PersonService.get(SomethingAboutCurrentRequest.CurrentOData); } 

And in PersonService :

 public IQueryable<Person> getPeople(var ODataQueries) { IQueryable<ServerSidePerson> serverPeople = from p in dbContext.ServerSidePerson select p; // Make the OData queries // Skip serverPeople = serverPeople.Skip(ODataQueries.Skip); // Take serverPeople = serverPeople.Take(ODataQueries.Take); // And so on // ... // Then, convert them IQueryable<Person> people = Converter.convertPersonList(serverPeople); return people; } 
+50
rest odata asp.net-web-api asp.net-mvc-4
May 28 '12 at 8:07
source share
4 answers

I just stumbled upon this old post and I am adding this answer as it is now very easy to handle OData requests. Here is an example:

 [HttpGet] [ActionName("Example")] public IEnumerable<Poco> GetExample(ODataQueryOptions<Poco> queryOptions) { var data = new Poco[] { new Poco() { id = 1, name = "one", type = "a" }, new Poco() { id = 2, name = "two", type = "b" }, new Poco() { id = 3, name = "three", type = "c" } }; var t = new ODataValidationSettings() { MaxTop = 2 }; queryOptions.Validate(t); //this is the method to filter using the OData framework //var s = new ODataQuerySettings() { PageSize = 1 }; //var results = queryOptions.ApplyTo(data.AsQueryable(), s) as IEnumerable<Poco>; //or DIY var results = data; if (queryOptions.Skip != null) results = results.Skip(queryOptions.Skip.Value); if (queryOptions.Top != null) results = results.Take(queryOptions.Top.Value); return results; } public class Poco { public int id { get; set; } public string name { get; set; } public string type { get; set; } } 
+36
Mar 19 '13 at 9:35 on
source share

The query from the URL is converted to a LINQ expression tree, which is then executed against the IQueryable returned by your operation. You can analyze the expression and present the results in any way. The downside is that you need to implement IQueryable, which is not very simple. Take a look at this blog series if you are interested: http://blogs.msdn.com/b/vitek/archive/2010/02/25/data-services-expressions-part-1-intro.aspx . It talks about WCF data services, but the filter expressions used by the web API will be very similar.

+3
May 28 '12 at 9:12 a.m.
source share

One way to use the Web-api will be with the client message handler http://www.asp.net/web-api/overview/working-with-http/http-message-handlers

Write your own handler as shown below:

 public class CustomHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { return base.SendAsync(request, cancellationToken).ContinueWith( (task) => { HttpResponseMessage response = task.Result; var persons = response.Content.ReadAsAsync<IQueryable<Person>>().Result; var persons2 = new List<Person>(); //This can be the modified model completely different foreach (var item in persons) { item.Name = "changed"; // here you can change the data //persons2.Add(....); //Depending on the results modify this custom model } //overwrite the response response = new HttpResponseMessage<IEnumerable<Person>>(persons2); return response; } ); } } 

Sign up to global.asax.cs

Method in application class:

 static void Configure(HttpConfiguration config) { config.MessageHandlers.Add(new CustomHandler()); } protected void Application_Start() { .... ..... //call the configure method Configure(GlobalConfiguration.Configuration); } 
+2
May 28 '12 at 10:07 a.m.
source share

I did something similar with WCF and asp.net mvc 3.5 data services, but that was a bit of a shred.

The first step is to rewrite the path so that skipping and vertex options are not applied twice, once by you and once at run time.

I rewrote the HttpModule. In the BeginRequest method, you will have the following code:

 HttpApplication app = (HttpApplication)sender; if (HttpContext.Current.Request.Path.Contains(YOUR_SVC)) { if (app.Request.Url.Query.Length > 0) { //skip questionmark string queryString = app.Request.Url.Query.Substring(1) .Replace("$filter=", "filter=") .Replace("$orderby=", "orderby=") .Replace("$top=", "top=") .Replace("$skip=", "skip="); HttpContext.Current.RewritePath(app.Request.Path, "", queryString); } } 

Then just browse the query line and pull out the necessary parameters.

 if (HttpContext.Current.Request.QueryString["filter"] != null) var filter = HttpContext.Current.Request.QueryString["filter"] as string; 

Then split the filter string and parse it in the sql statement or any other db commands. I used NHibernate in my case. I was also able to limit which filter commands I supported, which simplified the situation. For example, I did not do groupings. Mostly just comparison operators.

There is a list of filter operators in OData.org . Divide the string into "and" and "or" into separate sentences. Separate each sentence with a space, and you should get a 3-element array with the property name in [0] by the operator in [1] and the value in [2].

Articles will be in terms of Persons, but I assume that you can convert them to something that will output the correct results from db.

I ended up abandoning this approach, as it will not work for POSTS. I had to write my own Linq provider, but I wrote my own filter parser to make this easier to understand.

0
May 28 '12 at 12:24
source share



All Articles