Is there a better way in the new ASP.net MVC 4 WebApi to handle nested resources than setting up a special route for each one? (similar to here: ASP.Net MVC support for Nested Resources? - this was posted in 2009).
For example I want to handle:
/customers/1/products/10/
I have seen some examples of ApiController
actions named other than Get()
, Post()
etc, for example here I see an example of an action called GetOrder()
. I can't find any documentation on this though. Is this a way to achieve this?
EDIT: Although this answer still applies for Web API 1, for Web API 2 I strongly advise using Daniel Halan's answer as it is the state of the art for mapping subresources (among other niceties).
Some people don't like to use {action} in Web API because they believe that in doing so they will be breaking the REST "ideology"... I contend that. {action} is merely a construct that helps in routing. It is internal to your implementation and has nothing to do with the HTTP verb used to access a resource.
If you put HTTP verb constraints on the actions and name them accordingly you're not breaking any RESTful guidelines and will end up with simpler, more concise controllers instead of tons of individual controllers for each sub-resource. Remember: the action is just a routing mechanism, and it is internal to your implementation. If you struggle against the framework, then something is amiss either with the framework or your implementation. Just map the route with an HTTPMETHOD constraint and you're good to go:
You can handle these in the CustomersController like this:
You can also route a Create action on the same "resource" (orders):
And handle it accordingly in the Customer controller:
Yes, you still have to create a lot of routes just because Web API still can't route to different methods depending on the path... But I think it is cleaner to declaratively define the routes than to come up with a custom dispatching mechanisms based on enums or other tricks.
For the consumer of your API it will look perfectly RESTful:
GET http://your.api/customers/1/orders
(maps to GetOrders(long) returning all orders for customer 1)GET http://your.api/customers/1/orders/22
(maps to GetOrders(long, long) returning the order 22 for customer 1POST http://your.api/customers/1/orders
(maps to CreateOrder(long) which will create an order and return it to the caller (with the new ID just created)But don't take my word as an absolute truth. I'm still experimenting with it and I think MS failed to address properly subresource access.
I urge you to try out http://www.servicestack.net/ for a less painful experience writing REST apis... But don't get me wrong, I adore Web API and use it for most of my professional projects, mainly because it is easier to find programmers out there that already "know" it... For my personal projects I prefer ServiceStack.
Since Web API 2 you can use Route Attributes to define custom routing per Method, allowing for hierarchical routing
You also need to initialize Attribute Mapping in WebApiConfig.Register(),
Sorry, I have updated this one multiple times as I am myself finding a solution.
Seems there is many ways to tackle this one, but the most efficient I have found so far is:
Add this under default route:
This route will then match any controller action and the matching segment name in the URL. For example:
/api/customers/1/orders will match:
/api/customers/1/orders/123 will match:
/api/customers/1/products will match:
/api/customers/1/products/123 will match:
The method name must match the {action} segment specified in the route.
Important Note:
From comments
Since the RC you'll need to tell each action which kind of verbs that are acceptable, ie
[HttpGet]
, etc.I don't like using the concept of "actions" in the route of an ASP.NET Web API. The action in REST is supposed to be the HTTP Verb. I implemented my solution in a somewhat generic and somewhat elegant way by simply using the concept of a parent controller.
https://stackoverflow.com/a/15341810/326110
Below is that answer reproduced in full because I'm not sure what to do when one post answers two SO questions :(
I wanted to handle this in a more general way, instead of wiring up a ChildController directly with
controller = "Child"
, as Abhijit Kadam did. I have several child controllers and didn't want to have to map a specific route for each one, withcontroller = "ChildX"
andcontroller = "ChildY"
over and over.My
WebApiConfig
looks like this:My parent controllers are very standard, and match the default route above. A sample child controller looks like this:
Some drawbacks of my implementation
enum
, so I'm still having to manage parent controllers in two separate places. It could have just as easily been a string parameter, but I wanted to preventapi/crazy-non-existent-parent/5/comment/122
from working.There's probably a better solution that's even more general, but like I said, this works for me.