4

我已经使用 Web API 编写了一个 REST 服务,在阅读了 Brian Mulloy 的这个Web API 设计的部分之后,我试图弄清楚如何实现与 Web API 的关联。

Web API 设计摘录:

协会

资源几乎总是与其他资源有关系。在 WebAPI 中表达这些关系的简单方法是什么?

让我们再看看我们在名词中建模的 API 是好的,动词是坏的——与我们的狗资源交互的 API。请记住,我们有两个基本 URL:/dogs 和 dogs/1234。

我们使用 HTTP 动词来操作资源和集合。我们的狗属于主人。要获取属于特定所有者的所有狗,或为该所有者创建新狗,请执行 GET 或 POST:

获取 /owners/5678/dogs
发布 /owners/5678/dogs

现在,关系可能很复杂。主人与兽医有关系,与狗有关系,与食物有关系,等等。经常看到人们将这些链接在一起,使 URL 达到 5 或 6 级深度。请记住,一旦您拥有一个级别的主键,您通常不需要包含上述级别,因为您已经获得了您的特定对象。换句话说,你不应该需要太多比我们上面的 /resource/identifier/resource 更深的 URL 的情况。

所以我尝试为关联添加一个控制器方法,如下所示:

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

我还将路线模板修改为以下内容:

config.Routes.MapHttpRoute("ApiWithAssociations",
                           "api/{controller}/{id}/{action}");
config.Routes.MapHttpRoute("DefaultApi",
                           "api/{controller}/{id}",
                           new { id = RouteParameter.Optional });

不幸的是,当我更新/发布事件资源时,我现在得到一个 HTTP 500 内部服务器错误,响应正文说明

找到多个与请求匹配的操作

我已经尝试修改路由模板以及添加 System.Web.Http.HttpPostAttribute (和其他 HTTP 动词),但无济于事。

有没有人试过这个并让它工作?任何帮助,将不胜感激。如果绝对不可能有多个 http 动词,那么我想我将不得不放弃与我的 REST 服务的关联。

编辑:解决方案

使用 Radim Köhler 的回答,我能够得到这个工作。将 HttpGetAttribute 添加到 Guest 方法中,如下所示:

// GET api/event/5/guests
[HttpGet]
public IEnumerable<Guest> Guests(int id)
{
    // association code
}

并添加了一条附加路由来满足默认的 GET 操作,如下所示:

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

1 回答 1

2

解决方案,可能在显式 POST 映射中

只需添加新定义,它将用于事件/5 POST

// explicit Post() mapping 
config.Routes.MapHttpRoute(
    name: "DefaultPost",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { action = "Post" }
    , constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) }
    );

// existing
config.Routes.MapHttpRoute("ApiWithAssociations",
    "api/{controller}/{id}/{action}");
config.Routes.MapHttpRoute("DefaultApi",
    "api/{controller}/{id}",
    new { id = RouteParameter.Optional });
于 2013-05-30T17:24:50.843 回答