1

我正在 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,我无法绕过它。

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

4

1 回答 1

0

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

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

  • /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 }
    );
于 2013-08-13T20:19:56.297 回答