Search code examples
routesasp.net-web-api2odata

Specifying route values for OData with Web API


I'm working on a new OData project, and am trying to do it with Web API 2 for the first time. The OData feed was pretty simple to put in place, which was great in comparison to WCF.

The problem I have now is that my OData feed will be used in a "multi-tenant" environment and I would like to use "friendly" URLs for the feed depending on the tenant. Therefore, I would ideally need the feed URLs to look like this:

/store/tenant1/Products
/store/tenant2/Products

Both URLs are pointing to the same controller and ultimately the same dataset, but I would like to enforce some entity filtering based on the tenant. Apparently this is going to be difficult and somewhat different to standard Web API routing since I can only specify a route prefix and not a route template.

So far, I've modified my OData controller to take the tenant name as a parameter and this works great when hitting the following url (which is not exactly what I want, see target above):

http://mydomainname/odata/Products?tenantName=test

Using this route definition:

ODataConventionModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Product>("Products");
IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute(routeName: "OData", routePrefix: "odata", model: model);

And this is the sample action on my controller:

[Queryable]
public IQueryable<Product> GetPproducts(string tenantName)
{
    return _products.Where(p=>p.TenantName == tenantName);
}

I'm not quite sure if this is possible and my last resort will be to use URL rewrite rules, but I'd rather avoid this and have everything in code, done the right way.


Solution

  • After some investigation I found it works in this way: Just apply the route prefix name to the query, for example:

    public class MoviesController : ODataController
    {
        private MoviesContext _db = new MoviesContext();
    
        public IHttpActionResult Get()
        {
            var routeName=Request.ODataProperties().RouteName;
            ODataRoute odataRoute=Configuration.Routes[routeName] as ODataRoute;
            var prefixName = odataRoute.RoutePrefix;
            return Ok(_db.Movies.Where(m=>m.Title.StartsWith(prefixName)));
        }
        // Other methods here
    }
    

    Note: The above code is based on ODataActionsSample in https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/ Now OData v4 has become a standard of OASIS, but v3 is not, so v4 seems a good start point.