I wrote a REST service using the web API, and after reading the sections of this Web API Design by Brian Mulloy, I tried to figure out how I could implement associations with the Web API.
Retrieving Web API Design:
Associations
Resources are almost always related to other resources. What is an easy way to express this relationship in aWebAPI?
Let's look again at the API that we modeled in nouns, good, bad verbs - an API that interacts with our dog resource. Remember that we had two base URLs: / dogs and dogs / 1234.
We use HTTP verbs to work with resources and collections. Our dogs belong to the owners. Get all dogs belonging to a specific owner, or create a new dog for this owner, do GET or POST:
GET / owners / 5678 / dogs
POST / owners / 5678 / dogs
Now the relationship can be complicated. Owners have relationships with veterinarians who have relationships with dogs, who have relationships with food, etc. It is not uncommon to see how people build these together, creating URLs of 5 or 6 levels. Remember that as soon as you have a primary key for one level, you usually do not need to include levels above because you already have your own specific object. In other words, you should not need too many cases where the URL is deeper than ours above / Resources / identifier / resources.
So, I tried to add a controller method for the association, for example, as follows:
public class EventsController : ApiController { // GET api/events public IEnumerable<Event> Get() { // get list code } // GET api/events/5 public Event Get(int id) { // get code } // POST api/events public void Post([FromBody]Event evnt) { // add code } // POST api/events/5 public void Post(int id, [FromBody]Event evnt) { // update code } // DELETE api/events/5 public void Delete(int id) { // delete code } // GET api/events/5/guests public IEnumerable<Guest> Guests(int id) { // association code } }
I also changed the route patterns to the following:
config.Routes.MapHttpRoute("ApiWithAssociations", "api/{controller}/{id}/{action}"); config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional });
Unfortunately, when I do an update / message of the event resource, now I get an error of the internal HTTP 500 server with the response body, which indicates
We found a few actions matching the query
I tried modifying the route patterns in conjunction with the addition of System.Web.Http.HttpPostAttribute (and other HTTP verbs), but to no avail.
Has anyone tried this and earned it? Any help would be greatly appreciated. If it is absolutely impossible to have a multiplicity for the http verb, then I think I will have to give up associations with my REST service.
EDIT: SOLUTION
Using Radim Kรถhler's answer, I was able to get this work. Add the HttpGetAttribute to the Guests method like this:
// GET api/event/5/guests [HttpGet] public IEnumerable<Guest> Guests(int id) { // association code }
And he added an additional route to serve the default GET action, similar to the following:
config.Routes.MapHttpRoute("DefaultGet", "api/{controller}/{id}", new {action = "Get"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Get)}); config.Routes.MapHttpRoute("ApiWithAssociations", "api/{controller}/{id}/{action}"); config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new {id = RouteParameter.Optional});