4

As per OData Uri conventions http://www.odata.org/documentation/uri-conventions#FilterSystemQueryOption the following Uri is valid. http://services.odata.org/OData/OData.svc/Category(1)/Products?$top=2&$orderby=name

However the ASP.NET Web-API does not seem to support this (at least out of the box). It gives an error to the effect that it cannot find a controller called Category(1).

How to make this work with Web-API or is there a work around?

Vishal
  • 87
  • 1
  • 8
  • 1
    This is especially strange since the docs seem to imply that this is supported: https://learn.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/odata-routing-conventions. Perhaps support for this syntax has been added since this question was asked? – Nathan Friend May 03 '18 at 11:29

1 Answers1

5

I'm not sure why your question didn't garner attention when you asked it..but it's a real issue. The WebAPI supports a good deal of the OData spec, but not all of it to my understanding. Although, I have yet to find a resource describing precisely what aspects work. Out of the box the default routes support one layer of nesting, not the two like in your example. So something like :

mystuff.com/Category/5?$top2&orderby=name

With that said, i think you could craft something like what you want. I haven't compiled/tested all of this, so bear with me.... In global.asax setup a pair of routes like so:

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

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

Notice the order I registered the routes--put the category filter first. The corresponding controller for these routes would look something like : :

 public class ProductCategoryController : BaseAPIController {
     public IEnumerable<Product> Products(int catID, int id) {
         var result = new List<Product>();

         //do category filtering here...

          return result;
     }
 }

So the first route would allow application of a category filter (catID) and an optional ProductID (id). When the categoryID & parenthesis are excluded the 2nd route would catch. You need two routes because there isn't a great way to filter out the parenthesis automatically.

As a side note you may be interested in another answer I wrote that identifies some other route-gymnastics your probably going to encounter.

Community
  • 1
  • 1
EBarr
  • 11,826
  • 7
  • 63
  • 85
  • That's what I had tried earlier but didn't work. That is api/{controller}({resourceId})/{action} translates to api/Category(1)/products and an error is thrown with the message "No type was found that matches the controller named 'Category(1)'. – Vishal May 29 '12 at 22:34
  • Look at the answer a little closer. I tried the above and it worked for me. You need a PAIR of routes. One works when category is provided (and includes the parenthesis), one when it is not provided. NOTICE that the order I registered the routes. I suspect yours was the other way around. – EBarr May 29 '12 at 23:27
  • Yes that was the case. I could get it to work after changing the order of routes. Thank you so much! – Vishal May 30 '12 at 14:26