【问题标题】:WebAPI Conflicting routes for nested resources嵌套资源的 WebAPI 冲突路由
【发布时间】:2013-08-13 18:35:28
【问题描述】:

我正在 WebAPI(第一个)中开发一个 API,我在路由的特殊性方面遇到了一些问题,并将它们映射到我的控制器中用于嵌套资源的操作。

我有以下资源:

  1. 项目(根级资源)
  2. 网站(根级资源)
  3. 区域(站点的子资源)

所以我可以有一些时髦的嵌套,例如:

  • /api/projects/1/sites
  • /api/projects/1/sites/assigned
  • /api/sites/1/zones
  • /api/sites/1/zones/1

我遇到的问题是一些端点,特别是站点/区域的端点。我目前有两种 Get 方法,一种通过 siteId 和页面获取区域,另一种通过 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/sites/1/zones
  • /api/sites/1/zones/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,我无法绕过它。

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

【问题讨论】:

  • 你能发布你的整个路线配置吗?
  • 嗨,Rob,唯一缺少的是 WebAPI 概述的默认路由
  • 你的 PUT 方法是什么样的?
  • 现在离机,早上会添加put消息

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


【解决方案1】:

如果您的控制器方法的签名与您的路由匹配,它应该可以工作。

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

  • /api/sites/1/zones
  • /api/sites/1/zones/1

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

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

例如这会起作用:

    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 }
    );

【讨论】:

  • 我想知道是否需要 siteId,但由于它并没有真正用于单区域获取方法,所以感觉有点 hacky,将在早上测试
  • 这就是答案,感觉真的很恶心,在路由中包含一些在方法中什么都不做的东西,但要么我让我的控制器更细化,要么我想以这种方式工作。需要阅读更多关于路线的信息!
猜你喜欢
  • 2013-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-05
  • 2020-12-23
  • 1970-01-01
  • 1970-01-01
  • 2011-05-23
相关资源
最近更新 更多