ASP.NET Web API中的多个PUT方法

我有一个控制器Groups其中包含以下操作:

 public GroupModel Get(int ID) public GroupModel Post(CreateGroupModel model) public void Put(PublicUpdateGroupModel model) public void PutAddContacts(UpdateContactsModel model) public void PutRemoveContacts(UpdateContactsModel model) public void Delete(int ID) 

而我想要做的是使用标准的REST路由来调用标准的get,post,put,delete mehods。 但是如果动作名称被附加到url,则调用PutAddContactsPutRemoveContacts ,例如:

获取组/ – 调用Get方法

POST组/ – 调用Post方法

PUT组/ – 调用Put方法

DELETE groups / – 调用Delete方法

PUT groups / addcontacts – 调用PutAddContacts方法

PUT groups / removecontacts – 调用PutRemoveContacts方法

是否可以设置路由来执行此操作,或者如果我想在URL中使用操作名称,是否需要沿着RPC路由进行路由?

你现在有什么
要使用上述方法,您需要使用RPC。 这是因为你的例子已经完成了RPC的做事方式。 默认的WebAPI路由鼓励RESTful设置,但如果您对路由进行了微小的更改,一切都将开始工作。 例如,您可以将默认路由更改为典型的MVC路由:

 routes.MapRoute( name : "Default", url : "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }); 

添加路由后,以典型的MVC方式调用内容,使用控制器名称和操作。 但是,从你的问题来看,我怀疑你真的想成为RESTful,而不仅仅是让它工作所以请继续阅读……

保持沉默
REST 不需要HTTP ,尽管这两者经常在一起讨论。 REST实际上是关于具有语义准确表示的每个资源。 使用HTTP时,这意味着尊重HTTP语义的唯一URI。 因此,例如,使用HTTP GET的调用永远不应该修改数据,因为这违反了HTTP的GET定义和混淆HTTP基础设施(如缓存)。

POST / PUT与MERGE / PATCH
我们都熟悉GET,POST,PUT,HEAD等作为HTTP方法。 通常 ,GET用于检索,POST用于添加,PUT用于修改(尽管存在很多争议)。 但是,您有两种类型的修改:添加项目和从集合中删除项目。 PUT还是别的什么呢? 社区还没有完全确定如何做到这一点。

  • 选项1:自定义媒体类型 – HTTP规范确实允许所有排序方法,它是真正限制我们熟悉的子集的浏览器。 因此,您可以创建MERGE(Roy Fielding解决方法)或PATCH(oData解决方法)方法,并定义此新媒体类型的行为 – 可能是一个用于添加,一个用于删除。

  • 选项2:使用POST / PUT – 使用PUT添加和删除联系人。 只需将ID列表视为切换(如果存在删除,如果缺少添加)或者altatley包含足够的信息以便知道该怎么做。 然后返回HTTP 303,指示客户端它具有陈旧状态并刷新。

  • 选项3:完整列表 – 如果您的设置大小合理,则每次要更新时都可以随时传递完整的联系人列表。 这种方式逻辑是一个超级简单的擦除和替换。

从RESTful角度来看,真正重要的是您的应用程序在所有方法中以一致的方式运行。 因此,如果MERGE意味着添加,它应该始终意味着添加。 如果您希望将一组完整的ID传递给PUT,那么总是传递一套完整的ID。

控制器设计
如果是我,我会把你的控制器分成多个控制器。 一个控制器处理组另一个交易联系人(作为一个组),第三个处理组中的一个联系人。 就像是 …

 //api/Group/ public List Get() public GroupModel Get(int ID) public GroupModel Post(GroupModel model) //add a group public GroupModel Put(GroupModel model) //update a group (see comments above) public void Delete(int ID) //api/GroupContacts/ public ContactsModel Get() //gets complete list public void PostContacts(ContactsModel model) //pushes a COMPLETE new state public void Delete() //delete entire group of contacts //api/GroupContact/354/ public ContactModel Get(int id) //get contact id #354 public void PostContact(ContactModel model) //add contact (overwrite if exits) public void Delete(int id) //delete contact if exists 

如果您希望您的URL显示为嵌套(例如: /api/Group/Contacts/api/Group/Contact ),您可以查看我写的这篇文章 。 恕我直言,asp.net的路由需要调整以支持嵌套更容易……但这是一个不同的问题;-)

为了回应EBarr所说的,在Web API中进行分层路由可能会有点痛苦。 我在我的服务中经常使用它,所以我为Web API构建了替换路由服务。 Nuget在这里 ,源代码在GitHub上

这种路由方法要求您将URI名称空间构建为路径段的层次结构。 您可以将控制器连接到路径段树中的任意点,而不是将完整的URI模式与控制器匹配。

只是为了让您了解它的外观我创建了一个类似于您尝试的URI的小型自主样本:

 internal class Program { private static void Main(string[] args) { var baseAddress = new Uri("http://oak:8700/"); var configuration = new HttpSelfHostConfiguration(baseAddress); var router = new ApiRouter("api", baseAddress); // /api/Contacts router.Add("Contacts", rcs => rcs.To()); // /api/Contact/{contactid} router.Add("Contact", rc => rc.Add("{contactid}", rci => rci.To())); // /api/Group/{groupid} // /api/Group/{groupid}/Contacts router.Add("Group", rg => rg.Add("{groupid}", rgi => rgi.To() .Add("Contacts", rgc => rgc.To()))); configuration.MessageHandlers.Add(router); var host = new HttpSelfHostServer(configuration); host.OpenAsync().Wait(); Console.WriteLine("Host open. Hit enter to exit..."); Console.Read(); host.CloseAsync().Wait(); } } public class GroupController : TestController { } public class ContactsController : TestController { } public class ContactController : TestController { } public class GroupContactsController : TestController { } public class TestController : ApiController { public HttpResponseMessage Get() { var pathRouteData = (PathRouteData) Request.GetRouteData(); var paramvalues = new StringBuilder(); foreach (KeyValuePair keyValuePair in pathRouteData.Values) { paramvalues.Append(keyValuePair.Key); paramvalues.Append(" = "); paramvalues.Append(keyValuePair.Value); paramvalues.Append(Environment.NewLine); } var url = pathRouteData.RootRouter.GetUrlForController(this.GetType()); return new HttpResponseMessage() { Content = new StringContent("Response from " + this.GetType().Name + Environment.NewLine + "Url: " + url.AbsoluteUri + "Parameters: " + Environment.NewLine + paramvalues.ToString()) }; } } 

您应该能够将此代码粘贴到控制台应用程序中,并添加对Microsoft.AspNet.WebApi.SelfHost和Tavis.WebApiRouter nugets的引用并进行试用。 如果您对使用这种路由可以走多远感到好奇,那么这里有一个更复杂的示例。