asp.net web api routing design (multiple routes)

lapsus picture lapsus · Feb 6, 2013 · Viewed 9.2k times · Source

I'm currently designing a REST api with ASP.NET Web API.

I want to provide methods like these (http methods are irrelevant - should work for all):

  • /api/client
  • /api/client/CID1234 (CID1234 = Id)
  • /api/client/CID1234/orders (orders = action)

The problem is: Id should be optional - /api/client may return a list of clients Action should be optional too - in one case I want to get a specific client and in the other case I want to perform a certain action on that client. (RPC style)

I don't think I can use constraints because the "IDs" I am using look very different.

This approach didn't work:

config.Routes.MapHttpRoute(
            name: "RpcStyleApi",
            routeTemplate: "rest/{controller}/{action}",
            defaults: new { action = RouteParameter.Optional, id = RouteParameter.Optional }
        );

This approach didn't work neither:

config.Routes.MapHttpRoute(
            name: "RpcStyleApi",
            routeTemplate: "rest/{controller}/{action}"
        );

        config.Routes.MapHttpRoute(
            name: "RpcStyleApi2",
            routeTemplate: "rest/{controller}/{id}/{action}",
            defaults: new { action = RouteParameter.Optional }
        );

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "rest/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

This for example returns 404s because it cannot distinguish an "action" from an "id". "No action was found on the controller 'Client' that matches the name 'CID1234'.

To gain the flexibility I need should I go for a custom Actionselector? Or is it somehow possible using the Web API built in functionality? I understand that someone might want to use orders as a separate entity. But I might as well have real "actions" (not "entities") there. Like "lostpassword", "changepassword" and so forth..

Thank you for your advice

P.S.: Something like described here is not exactly something I'd be willing to do. Bad api design.

Answer

Amgad Fahmi picture Amgad Fahmi · Mar 30, 2015

This is actually one of the best practice while build restful API, here is a little video about a different aspects to be considered while build the API https://www.youtube.com/watch?v=ZpqCN8iO9Y8

Solution: You don't need to change your mapping route, all what you need is used the method attributes on top of each method like the following

\\ api/clients [this should be clients not client if its returning a list]
[Route("api/clients")]
public IHttpActionResult Get()
{
   return Ok("here you list of clients");
}

/api/client/CID1234 (CID1234 = Id)
[Route("api/clients/{Id}")]
public IHttpActionResult Get(string Id)
{
   return Ok("use the Id to retrieve the client details");
}

/api/client/CID1234/orders (orders = action)
[Route("api/clients/{Id}/orders")]
public IHttpActionResult GetOrders(string Id)
{
   return Ok("use the Id to retrieve the client orders");
}

About the optional, now you build your own logic inside the method as you get its values.

This article have more details about this point

https://aspnetwebstack.codeplex.com/wikipage?title=Attribute%20Routing%20in%20MVC%20and%20Web%20API

PS: This is using .Net framework 4.5.2 using owin