WebAPI嵌套资源的冲突路由

时间:2013-08-13 18:35:29

标签: asp.net asp.net-mvc asp.net-web-api

我正在开发WebAPI(First One)中的API,我遇到了路由特定问题,并将它们映射到控制器中针对嵌套资源的操作。

我有以下资源:

  1. 项目(根级资源)
  2. 网站(根级资源)
  3. 区域(站点的子资源)
  4. 所以我可以有一些时髦的嵌套:

    • / API /项目/ 1 /位点
    • / API /项目/ 1 /位点/分配
    • / API /位点/ 1 /区
    • / API /位点/ 1 /区域/ 1

    我遇到的问题是一些终点,特别是站点/区域终点。我目前有两个Get方法,一个是通过siteId和page获取区域,另一个是通过Id获取区域(下面的代码很乱,我需要一个重构但只是试图让我的头围绕WebAPI)。

        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<Zone, ZoneRepresentation>();
    
            resp.Content = new ObjectContent<ZoneRepresentation>(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<Zone>(pageSize);
    
            if (pagingInfo.TotalRecords > 0)
            {
                if (page > 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 => z.Name)
                             .Paging(page, DefaultPage, pageSize)
                             .ToPagedRepresentation<Zone, ZoneListRepresentation>(pagingInfo.TotalRecords, pagingInfo.TotalPages, page, new Link("Zones", "/sites/" + siteId.ToString() + "/zones?page={page}"));
    
                resp.Content = new ObjectContent<ZoneListRepresentation>(zones, new JsonHalMediaTypeFormatter());
            }
            else
            {
                resp.StatusCode = HttpStatusCode.NoContent;
                throw new HttpResponseException(resp);
            }
    
            return resp;
        }
    

    如果没有指定其他路线,则这些路线不起作用,因此尝试访问:

    • / API /位点/ 1 /区
    • / API /位点/ 1 /区域/ 1

    将导致404。

    为了让第一条路线起作用,我添加了以下路线配置(我确信有一种更通用的方法,但只是想让它尽可能简单地工作):

            config.Routes.MapHttpRoute(
                name: "SiteZonesApiRoute",
                routeTemplate: "api/sites/{siteId}/zones/{id}",
                defaults: new { Controller = "Zones", id = RouteParameter.Optional }
            );
    

    哪个适用于第一个链接,但传入ID我得到:

    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
    

    好的,我有两个GET方法,虽然一个接受一个int而另外三个,所以我很困惑为什么它变得混乱,但我认为这是路由一个全新的概念。然后我做的是为ID类型添加一个非常具体的路由处理程序,并指向特定的操作方法:

            config.Routes.MapHttpRoute(
                 name: "ZoneByIdApiRoute",
                 routeTemplate: "api/sites/{siteId}/zones/{id}",
                 defaults: new { Controller = "Zones", action="Get" }
             );
    

    很棒,有效......不是因为某些原因,非常具体的路线完全打破了PUT方法,现在总是返回405而我无法绕过它。

    能否清楚地看到我在这里做错了什么?我可以创建更多的控制器,但这似乎传递了问题,我完全卡住了。

1 个答案:

答案 0 :(得分:0)

如果您的控制器方法的签名与您的路线匹配,它应该有效。

您写道,您希望匹配以下路线:

  • / api / sites / 1 / zones
  • / API /位点/ 1 /区域/ 1

即,siteId是必需参数,id(zoneId)是可选的。

这意味着你应该有一个名为Get或GetSomething的控制器方法,它将siteId和id作为参数。 Id可以是可选的,或者您可以使用另一个只有siteId作为参数的方法。

E.g。这会奏效:

    public class ZonesController : ApiController
    {
    // /api/sites/1/zones/1
    public HttpResponseMessage Get(int siteId, int id)
    {
        return Request.CreateResponse<string>(HttpStatusCode.OK, string.Format("siteId: {0}. Id: {1}", siteId, id));
    }

    // /api/sites/1/zones?page=12&pageSize=30
    public HttpResponseMessage GetByPage(int siteId, int page = 0, int pageSize = 10)
    {
        return Request.CreateResponse<string>(HttpStatusCode.OK, string.Format("siteId: {0}. page: {1}", siteId, page));
    }
}

路线配置:

config.Routes.MapHttpRoute(
        name: "SiteZonesApiRoute",
        routeTemplate: "api/sites/{siteId}/zones/{id}",
        defaults: new { Controller = "Zones", id = RouteParameter.Optional }
    );