WebApi routing integration and RESTful URI style

Topics: Web Api
Jun 6, 2011 at 5:42 PM

Why integration with Routing is so inflexible? Let's say we have REST API URI templates:

/companies /company/{companyid}

/company/{companyid}/departments

/company/{companyid}/department/{departmentid}

/company/{companyid}/department/{departmentid}/staff

/company/{companyid}/department/{departmentid}/employee/{employeeid}

What I mean here is two, three and more levels of entities nesting. This kind of URI's is usual for RESTful architecture style. I see no way to handle company(ies), department(s), employee and other possible nested entities with different handlers. Or, probably, I missed something?

 

RouteTable.Routes.MapServiceRoute("companies", config); // it's ok
RouteTable.Routes.MapServiceRoute("company", config); // still ok

// no way. prefix should be constant, no {companyid}
RouteTable.Routes.MapServiceRoute("company/{companyid}/department", config);

 

Method that handles this impossible route, can look like the one below:

 

[WebGet(UriTemplate = "{departmentid}")]
public Department Get(int companyid, int departmentid)

 

The only way for now is to have all methods in single handler:

 

[ServiceContract]
public class AllInOneHandler
{
	[WebGet(UriTemplate = "/company/{companyid}")]
	public Company GetCompany(int companyid) {}

	[WebGet(UriTemplate = "/company/{companyid}/departments")]
	public IQueryable GetDepartments(int companyid) {}

	[WebGet(UriTemplate = "/company/{companyid}/department/{departmentid}")]
	public Department GetDepartment(int companyid, int departmentid) {}
	// and dozens of other methods to cover the whole API
}

 

This works, but bad for a lot of obvious reasons.

Coordinator
Jun 6, 2011 at 5:52 PM

Hi Rossi

It's a known issue. WCF's ServiceHost currently requires you to provide a base address which is static / cannot be parameterized. ServiceRoute uses a ServiceHost under the hood thus it is also constrained in a similar manner. Based on that at this point in time using a service such as the one above is the easiest way to address this. We are looking into various possible solutions for addressing this in a more elegant matter. There are things you can do now by create a placeholder contract which you add operations to, but it's non-trival. I am working on a prototype which I will put out there shortly which shows how to do this.

What i would suggest for now is to consider splitting out your sub resource into a seperate classes and make the metthods in your AllInOneHandler be just a facade for delegating off to your handler. Using the IoC integration you can inject an ICompanyDepartmentsHandler / ICompanyDepartmentHandler which you delegate to. You can also put the facade methods into a partial class so as not to pollute your main class file.

Thanks for your patience.

 

 

Coordinator
Jun 6, 2011 at 6:08 PM

Hi Rossi

I refactored your code above to show what I mean. It's a bit more work but not major. You'll notice below I'm injecting a dept and depts handler. I then added methods in a partial class which invoke those handlers.

    [ServiceContract]
    public partial class CompanyHandler
    {
        ICompanyDepartmentHandler departmentHandler;
        private ICompanyDepartmentsHandler departmentsHandler;

        public CompanyHandler(ICompanyDepartmentHandler departmentHandler, ICompanyDepartmentsHandler departmentsHandler)
        {
            this.departmentHandler = departmentHandler;
            this.departmentsHandler = departmentsHandler;
        }

        [WebGet(UriTemplate = "/company/{companyid}")]
        public Company GetCompany(int companyid) { }

    }
    
    public partial class CompanyHandler
    {
        [WebGet(UriTemplate = "/company/{companyid}/departments")]
        public IQueryable<Department> GetDepartments(int companyid)
        {
            return this.GetDepartments(companyid);

        }

        [WebGet(UriTemplate = "/company/{companyid}/department/{departmentid}")]
        public Department GetDepartment(int companyid, int departmentid)
        {
            return this.GetDepartment(companyid, departmentid);

        }	// and dozens of other methods to cover the whole API
    }

Jun 6, 2011 at 9:01 PM

Hi

Thank you for reply

That's good refactoring for static API, but what I really need is ability to extend list of resources that available via REST (plugins).  In my case placeholder contract which I can add operations to at runtime seems to be a solution.

Coordinator
Jun 6, 2011 at 9:04 PM

Gotcha. I will try to get the protoype out there now that I have a few weeks of no travel :-)

Jun 6, 2011 at 9:10 PM

That's great, thank you :-)

Jun 7, 2011 at 7:20 PM
Edited Jun 7, 2011 at 8:06 PM

Just wanted to chime in and vote this up as important. This is the exact same barrier I came up on during my very first day working with the new API. I think this is going to be a pretty common pain point. So, hopefully you guys can come up with an elegant solution in the near future.

Jan 17, 2012 at 9:02 PM

Is anyone able to please update the community on the status for support of parameterized route prefixes.  Like stated in this post, we're facing a scenario where our API URI templates would look like the following:

/sites
/sites/{siteId}/users
/sites/{siteId}/groups
/sites/{siteId}/groups/{groupId}/users

This seems to be an on-going problem and creating one giant facade is not ideal. Thanks!

Coordinator
Jan 17, 2012 at 9:59 PM

Full support for ASP.NET Routing including parameterized routes and route constraints is coming with the MVC integration.

Daniel Roth

Jan 17, 2012 at 10:23 PM

That is some great news!  Thanks for the update Dan.

Jan 18, 2012 at 3:19 PM
danroth27 wrote:

Full support for ASP.NET Routing including parameterized routes and route constraints is coming with the MVC integration.

 

Daniel Roth

 


That is truly awesome news.... I dread to ask but any idea of timeframe :)?

Coordinator
Jan 18, 2012 at 7:22 PM

We are working on getting a Beta of the bits out in Feb.

Daniel Roth