Multi-Level Routing in ASP.NET web API without Areas

Go To StackoverFlow.com

4

I'm trying to use the new ASP.NET Web API to do the following:

A GET to /api/business/{id} gets the business's directory information. A PUT to the same URL updates the information, etc. That part works fine. The problem I'm having comes when I want to add what would formerly have been an action, to view reviews of that business. Ideally I want to be reachable at /api/business/{id}/reviews, where a GET returns the reviews, a POST submits a new review, and so forth.

In a normal ASP.NET MVC application, I could define two action methods (Reviews and PostReview) then used attributes to change the action name and accepted HTTP method of the second function. I would expect there's some way to do this with classes in the new system, but I can't figure out what it is and I'm not seeing anything about the issue in the documentation.

Using areas does not work, as best I can tell: I need the /business/{id} URL to work and with areas that breaks.

2012-04-05 15:47
by ehdv


13

Web API does support routing by action, so you can do something like this:

routes.MapHttpRoute(
    name: "ActionApi",
    routeTemplate: "api/{controller}/{id}/{action}",
    defaults: new { action = "DirectoryInfo", id = RouteParameter.Optional }
);

Controller methods:

[HttpGet]
[ActionName("DirectoryInfo")]
public void GetDirectoryInfo(int id)

[HttpPut]
[ActionName("DirectoryInfo")]
public void PutDirectoryInfp(int id)

[HttpGet]
public void Review(int id)

[HttpPost]
[ActionName("Review")]
public void PostReview(int id)

Another option is to create two controllers, BusinessController and ReviewController, and create the following routes:

routes.MapHttpRoute(
    name: "BusinessRoute", 
    routeTemplate: "api/business/{id}", 
    defaults: new { controller = "Business", id = RouteParameter.Optional }
    );
routes.MapHttpRoute(
    name: "reviewsRoute", 
    routeTemplate: "api/contacts/{id}/reviews/", 
    defaults: new { controller = "Reviews", id = RouteParameter.Optional}
    );

and then use Web API-style implicit action names.

2012-04-06 18:04
by Mike Wasson
three upvotes?.. these are the stone tablets, folk - nik.shornikov 2013-01-25 05:21


1

From DDD point of view, if review is an aggregate (it has its own repository) then it needs its own controller. If Post is its root aggregate, then it must be returned with the Post itself.

Form what I can see, it is a root aggregate hence it requires its own Controller. Hence you have:

/api/reviews/{id}

OR

/api/reviews?postId={postId}

where id is that of post.


This is mainly caused by the fact that we do not have actions anymore. I am not entirely happy with this, but well that is what we have.

2012-04-05 15:56
by Aliostad
And there's no way to use routing or anything to simulate them? I'm fine with 1 action per class replacing 1 action per method, as long as there's a way to route to them elegantly.

Also, there may end up also being /api/reviews but having the set of reviews also be accessible from the business seems more intuitive to me - ehdv 2012-04-05 16:08

@ehdv glenn block had two controllers in his example, one for the entity the other for its collections. So it is how he intended but perhaps action will be added later - Aliostad 2012-04-05 16:11


1

Using areas does not work, as best I can tell: I need the /business/{id} URL to work and with areas that breaks.

For the record, you can get Areas and WebApi to behave appropriately by overriding the DefaultHttpControllerSelector of ASP.NET MVC. I've tackled this very issue in ASP.NET MVC at work. Please see the following blog for my implementation and further details on how to introduce Area support to WebApi:

http://blogs.infosupport.com/asp-net-mvc-4-rc-getting-webapi-and-areas-to-play-nicely/

2012-08-16 18:02
by Martin Devillers
Ads