我使用Web API编写了一个REST服务,在阅读了Brian Mulloy的Web API设计部分之后,我试图弄清楚如何实现与Web API的关联。
Web API设计提取:
协会
资源几乎总是与其他资源有关系。 在WebAPI中表达这些关系的简单方法是什么?
让我们再看看我们在名词中建模的API是好的,动词是坏的-theAPI与我们的狗资源相互作用。 请记住,我们有两个基本URL:/ dogs和dogs / 1234。
我们使用HTTP动词来操作资源和集合。 我们的狗属于所有者。 要获得属于特定所有者的所有狗,或为该所有者创建新狗,请执行GET或POST:
GET / Owner / 5678 /狗
邮寄/拥有者/ 5678 /狗现在,关系可能很复杂。 业主与兽医有关系,兽医与狗有关系,与食物有关系,等等。 通常会看到人们将这些字符串组合在一起,形成5或6级的URL。 请记住,一旦拥有一个级别的主键,通常不需要包含上面的级别,因为您已经拥有了特定的对象。 换句话说,您不应该需要太多的情况,其中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服务的关联。
编辑:解决方案
使用RadimKöhler的答案,我能够实现这一点。 将HttpGetAttribute添加到Guests方法,如下所示:
// 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});
解决方案可以在显式POST映射中
只需添加新定义,该定义将用于events / 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 });