Diego Barros
Diego Barros

Reputation: 2068

REST API nested resources should be handled by which controller?

Let's say I have a REST API endpoint: users/123/orders/234.

As the URI implies, I am returning order 234 for user 123. My question is, which controller should handle the request?

Should it be an action in the UsersController, something like GetOrder(int userId, int orderId)? Or should it be handled in the OrdersController with something like GetOrder(int id)?

Is it just a matter of taste, or is one way more "correct" than the other?

Upvotes: 9

Views: 3059

Answers (3)

HelloThere
HelloThere

Reputation: 1040

My point is to have separate controllers for these nested resources and use default action names. For example: api/users/123/orders - in UserOrdersController@index

That way by just looking at controller names, you know which entity the route operates on (User in this case) and which entity is being fetched (Order in this case)

One drawback/inconvenience for this method is that you can't use the same abstract controller as ModelController if you have one. because index in normal ModelController doesn't have parameters while index in this kind of conjunct controllers needs id of the entity model it operates on.

Upvotes: 0

gideon
gideon

Reputation: 19465

Well I would advocate you should go by the entity that is being fetched.

In your case What is being fetched? : Orders -> So OrdersController.

If Users were being fetched given a particular order id then it would be a UsersController.

You should have a look at the stackexchange api for good examples : http://api.stackexchange.com/docs

There are numerous actions, but they're each grouped by the entity they operate on, and I bet that's the controller they are in.


There is no inbuilt setup for this route. You could do the following :

This is a specific route.

routes.MapHttpRoute(
    name: "users_orders",
    routeTemplate: "api/{controller}/{user_id}/Orders/{order_id}",
    defaults: new
    {
        controller = "Orders",
        action = "FetchByUser"
    });

Which would need an action method like this:

public ActionResult FetchByUser(int user_id, int order_id)
{
}

You could try doing a more generalised route like this:

routes.MapHttpRoute(
    name: "fetch_route",
    routeTemplate: "api/{controller}/{id1}/{type}/{id2}",
    defaults: new
    {
        action = "Fetch"
    });

And the action method would be:

public ActionResult Fetch(int user_id, string type, int order_id)
{
}

Note: I would interpret this -> users/123/orders/234 as for the user 123 get the order 234. If like @karan says you don't need the user context then you should not have this method. I'm not sure about your design or requirements here.

Upvotes: 14

Kiran
Kiran

Reputation: 57949

My 2 cents. You could do something like below:

api/users/123/orders - goes to UsersController and retrieves all orders of this particular user 123 only, where as api/orders, which goes to the OrdersController would give all the orders in the system.

api/users/123/orders/234 - do not even support this uri space...here since user is trying to access details about the order 234, they should use api/orders/234 which should take to Orders controller.

Upvotes: 3

Related Questions