title: WebAPI Conflicting routes for nested resources
body: <p>I am developing an API in WebAPI (First One) and i am having some issues with the specifity of routes and mapping them to the actions in my controllers for nested resources.</p>
  
  <p>I have the following resources:</p>
  
  <ol>
  <li>Projects (root level resource)</li>
  <li>Sites (root level resource)</li>
  <li>Zones (Sub resource of Site)</li>
  </ol>
  
  <p>so i can have some funky nesting like:</p>
  
  <ul>
  <li>/api/projects/1/sites</li>
  <li>/api/projects/1/sites/assigned</li>
  <li>/api/sites/1/zones</li>
  <li>/api/sites/1/zones/1</li>
  </ul>
  
  <p>The problem i am having is with some of the end points, specifically the site/zone ones.  I currently have two Get methods, one to get zones by siteId and page, and the other to get a Zone by Id (Code below is messy, i needs a refactor but just trying to get my head round WebAPI).</p>
  
  <pre><code>    public HttpResponseMessage Get(int id)
      {
          var resp = new HttpResponseMessage(HttpStatusCode.OK);
  
          var zone = (from z in _repo
                      where z.Id == id
                      select z)
              .SingleOrDefault()
              .ToRepresentation&lt;Zone, ZoneRepresentation&gt;();
  
          resp.Content = new ObjectContent&lt;ZoneRepresentation&gt;(zone, new JsonHalMediaTypeFormatter());
  
          return resp;
      }
  
      public HttpResponseMessage GetByPage(int siteId, int page = DefaultPage, int pageSize = PageSize)
      {
          var resp = new HttpResponseMessage(HttpStatusCode.OK);
  
          //TODO: Make sure access to project is checked....
  
          var pagingInfo = (from z in _repo
                            where z.Site.Id == siteId
                            select z)
                            .GetPagingInfo&lt;Zone&gt;(pageSize);
  
          if (pagingInfo.TotalRecords &gt; 0)
          {
              if (page &gt; pagingInfo.TotalPages)
              {
                  resp.StatusCode = HttpStatusCode.NotFound;
                  throw new HttpResponseException(resp);
              }
  
              var zones = (from z in _repo.Include("Site")
                           where z.Site.Id == siteId
                           select z)
                           .OrderBy(z =&gt; z.Name)
                           .Paging(page, DefaultPage, pageSize)
                           .ToPagedRepresentation&lt;Zone, ZoneListRepresentation&gt;(pagingInfo.TotalRecords, pagingInfo.TotalPages, page, new Link("Zones", "/sites/" + siteId.ToString() + "/zones?page={page}"));
  
              resp.Content = new ObjectContent&lt;ZoneListRepresentation&gt;(zones, new JsonHalMediaTypeFormatter());
          }
          else
          {
              resp.StatusCode = HttpStatusCode.NoContent;
              throw new HttpResponseException(resp);
          }
  
          return resp;
      }
  </code></pre>
  
  <p>With no additional routes specified these do not work, so trying to access:</p>
  
  <ul>
  <li>/api/sites/1/zones</li>
  <li>/api/sites/1/zones/1</li>
  </ul>
  
  <p>will result in 404's.  </p>
  
  <p>To get the first route to work i add the following route configuration (i am sure there is a more general approach but just want to get this working as simply as possible):</p>
  
  <pre><code>        config.Routes.MapHttpRoute(
              name: "SiteZonesApiRoute",
              routeTemplate: "api/sites/{siteId}/zones/{id}",
              defaults: new { Controller = "Zones", id = RouteParameter.Optional }
          );
  </code></pre>
  
  <p>Which works for the first link, but passing in an ID i get:</p>
  
  <pre><code>ExceptionMessage":"Multiple actions were found that match the request:     
  System.Net.Http.HttpResponseMessage Get(Int32) on type API.Controllers.ZonesController
  System.Net.Http.HttpResponseMessage GetByPage(Int32, Int32, Int32) on type 
  API.Controllers.ZonesController
  </code></pre>
  
  <p>Ok fine, i have two GET methods, although one accepts one int and the other three, so i am little confused as to why it's getting muddled but i think that is down to routes an all being a new concept.  What i then do is add a very specific route handler for the ID type and pointing to the specific action method:</p>
  
  <pre><code>        config.Routes.MapHttpRoute(
               name: "ZoneByIdApiRoute",
               routeTemplate: "api/sites/{siteId}/zones/{id}",
               defaults: new { Controller = "Zones", action="Get" }
           );
  </code></pre>
  
  <p>Great, that worked......Well not really as for some reason, the very specific route completely breaks the PUT method, with always now returns a 405 and i can't get around it.</p>
  
  <p>Is it clear to see what i have done wrong here?  I could create more controllers but that seems like passing the problem around and i am completely stuck.</p>