2012-07-15 24 views
0

我正在开发一个基于MVC4 RC的Web API,并使用数据库优先实体框架作为我的模型。 我有2个实体是ItemGroup。 这两个实体之间存在多对多的关系。关联绑定/解除绑定的HTTP方法

现在,经过很容易实现两个CRUD操作的API,使用标准的HTTP方法(GETPOSTPUTDELETE),我来到,我要实现的绑定和解除绑定的项目点往返于团体。

我已经尝试其他动词,如LOCKUNLOCK,没有成功(他们似乎不支持他们),并试图以某种方式操纵POSTPUT命令,再次无功而返。

你们任何一个好人都有一个想法如何实现这一点?

非常感谢!

回答

1

您可以将多对多表示为根资源上的子集合。例如。您有/ items/1234和/ groups/4567 - 您可以将组作为子集合/ item/1234/groups或/ groups/4567/items

无论哪种方式都是同样有效的。我通常去的路线使用PUT设置的关系和DELETE将其删除 - 有些人会说,这不是真正的REST,但它在我在使用它的情况下工作得很好

PUT /items/1234/groups/4567 - 创建关系项目1234和组4567之间 DELETE /items/1234/groups/4567 - 删除项目1234和组4567之间的关系

这篇文章帮了我很多。当我看着这最后一...

How to handle many-to-many relationships in a RESTful API?

更新:路由

所以对于这些更复杂的情况,我们已经结束了单纯用更具体的路线。它可能会很快变得丑陋,试图将所有东西塞进一个通用路线。我们有一套单元测试,确保相关URL被路由到正确的控制器和操作。

// routes 
    routes.MapHttpRoute(
     name: "items.groups", 
     routeTemplate: "items/{itemId}/groups/{groupId}", 
     defaults: new { controller = "ItemGroup", groupId = RouteParameter.Optional }); 

ItemGroupController具有Get,Delete和Put方法。这样,我们的单元测试...

// unit tests 
    [Test] 
    public void PutItemGroup() 
    { 
     RoutingResult routingResult = this.GenerateRoutingResult(HttpMethod.Put, "~/items/1234/groups/4567"); 
     Assert.IsNotNull(routingResult); 
     Assert.AreEqual("ItemGroup", routingResult.Controller); 
     Assert.AreEqual("Put", routingResult.Action); 
     Assert.AreEqual("1234", routingResult.RouteData.Values["itemId"]); 
     Assert.AreEqual("4567", routingResult.RouteData.Values["groupId"]); 
    } 

    [Test] 
    public void GetItemGroups() 
    { 
     RoutingResult routingResult = this.GenerateRoutingResult(HttpMethod.Get, "~/items/1234/groups"); 
     Assert.IsNotNull(routingResult); 
     Assert.AreEqual("ItemGroup", routingResult.Controller); 
     Assert.AreEqual("GetAll", routingResult.Action); 
     Assert.AreEqual("1234", routingResult.RouteData.Values["itemId"]); 
    } 

    [Test] 
    public void GetItemGroup() 
    { 
     RoutingResult routingResult = this.GenerateRoutingResult(HttpMethod.Get, "~/items/1234/groups/4567"); 
     Assert.IsNotNull(routingResult); 
     Assert.AreEqual("ItemGroup", routingResult.Controller); 
     Assert.AreEqual("Get", routingResult.Action); 
     Assert.AreEqual("1234", routingResult.RouteData.Values["itemId"]); 
     Assert.AreEqual("4567", routingResult.RouteData.Values["groupId"]); 
    } 

    [Test] 
    public void DeleteItemGroup() 
    { 
     RoutingResult routingResult = this.GenerateRoutingResult(HttpMethod.Delete, "~/items/1234/groups/4567"); 
     Assert.IsNotNull(routingResult); 
     Assert.AreEqual("ItemGroup", routingResult.Controller); 
     Assert.AreEqual("Delete", routingResult.Action); 
     Assert.AreEqual("1234", routingResult.RouteData.Values["itemId"]); 
     Assert.AreEqual("4567", routingResult.RouteData.Values["groupId"]); 
    } 

    private RoutingResult GenerateRoutingResult(HttpMethod method, string relativeUrl) 
    { 
     HttpConfiguration httpConfiguration = new HttpConfiguration(this.HttpRoutes); 
     HttpRequestMessage request = new HttpRequestMessage(method, string.Format("http://test.local/{0}", relativeUrl.Replace("~/", string.Empty))); 
     IHttpRouteData routeData = this.HttpRoutes.GetRouteData(request); 

     Assert.IsNotNull(routeData, "Could not locate route for {0}", relativeUrl); 

     this.RemoveOptionalRoutingParameters(routeData.Values); 

     request.Properties.Add(HttpPropertyKeys.HttpRouteDataKey, routeData); 
     request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, httpConfiguration); 

     IHttpControllerSelector controllerSelector = new DefaultHttpControllerSelector(httpConfiguration); 
     HttpControllerContext controllerContext = new HttpControllerContext(httpConfiguration, routeData, request) 
      { 
       ControllerDescriptor = controllerSelector.SelectController(request) 
      }; 

     HttpActionDescriptor actionDescriptor = controllerContext.ControllerDescriptor.HttpActionSelector.SelectAction(controllerContext); 
     if (actionDescriptor == null) 
     { 
      return null; 
     } 

     return new RoutingResult 
      { 
       Action = actionDescriptor.ActionName, 
       Controller = actionDescriptor.ControllerDescriptor.ControllerName, 
       RouteData = routeData 
      }; 
    } 

    private void RemoveOptionalRoutingParameters(IDictionary<string, object> routeValueDictionary) 
    { 
     int count = routeValueDictionary.Count; 
     int index1 = 0; 
     string[] strArray = new string[count]; 
     foreach (KeyValuePair<string, object> keyValuePair in routeValueDictionary) 
     { 
      if (keyValuePair.Value == RouteParameter.Optional) 
      { 
       strArray[index1] = keyValuePair.Key; 
       ++index1; 
      } 
     } 

     for (int index2 = 0; index2 < index1; ++index2) 
     { 
      string key = strArray[index2]; 
      routeValueDictionary.Remove(key); 
     } 
    } 

    private class RoutingResult 
    { 
     public string Controller { get; set; } 

     public string Action { get; set; } 

     public IHttpRouteData RouteData { get; set; } 
    } 

干杯, 院长

+0

谢谢院长!我现在遇到了路由规则的问题。我已经使用了/ api/{controller}/{id}/{action}/{id2},其id,action和id2是可选的,意图在/ api/groups/1/items/2上放置一个PUT动词路由到我的绑定方法,并在同一个DELETE之一将路由到解除绑定方法。似乎无法得到这个工作。任何线索? – 2012-07-15 14:33:43

+0

哈哈,最近一直很有趣,对每一个案件做出一般规定都可能是痛苦的!我们最终将我们分成了许多处理这种特定情况的规则。 WCF Web Api(当前框架的前身)中的基于属性的路由使得这种流程更好。我将编辑响应以添加一些我们最终使用的示例路由... – 2012-07-15 14:41:42

+0

谢谢,Dean!这真的有帮助! – 2012-07-15 15:00:59