我有一个控制器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。但是,如果操作名称附加到网址,请调用PutAddContacts
和PutRemoveContacts
,例如:
GET groups / - 调用Get方法
POST组/ - 调用Post方法
PUT groups / - 调用Put方法
删除组/ - 调用删除方法
PUT groups / addcontacts - 调用PutAddContacts方法
PUT groups / removecontacts - 调用PutRemoveContacts方法
是否可以设置路由来执行此操作,或者如果我想在URL中使用操作名称,是否需要沿着RPC路由进行路由?
答案 0 :(得分:12)
你现在拥有什么
要使用上述方法,您需要使用RPC。这是因为你的例子已经完成了RPC的做事方式。默认的WebAPI路由鼓励RESTful设置,但如果您对路由进行了微小的更改,一切都将开始工作。例如,您可以将默认路由更改为典型的MVC路由:
routes.MapRoute( name : "Default",
url : "{controller}/{action}/{id}",
defaults: new { controller = "Home",
action = "Index",
id = UrlParameter.Optional });
添加路线后,以典型的MVC方式调用,使用控制器名称&行动。但是,从您的问题来看,我怀疑您确实希望成为RESTful,而不仅仅是让它工作,所以请继续阅读......
RESTful
REST doesn't require HTTP,虽然这两者经常在一起讨论。 REST实际上是关于具有语义准确表示的每个资源。使用HTTP时,这意味着尊重HTTP语义的唯一URI。因此,例如,使用HTTP GET的调用永远不应该修改数据,因为这违反了HTTP的GET定义和混淆HTTP基础设施,如缓存。
POST / PUT vs MERGE / PATCH
我们都熟悉GET,POST,PUT,HEAD等作为HTTP方法。 通常,GET用于检索,POST用于添加,PUT用于修改(尽管有很多争议)。但是,您有两种类型的修改:添加项目和从集合中删除项目。 PUT还是别的什么呢?关于如何执行此操作的社区hasn't quite settled。
选项1:自定义媒体类型 - HTTP规范确实允许所有排序方法,它是真正限制我们熟悉的子集的浏览器。因此,您可以创建MERGE (a Roy Fielding work around) or PATCH (an oData work around)方法并定义此新媒体类型的行为 - 可能是一个用于添加,另一个用于删除。
选项2:使用POST / PUT - 使用PUT添加和删除联系人。只需将ID列表视为切换(如果存在则删除,如果缺少添加)或者altatley包含足够的信息以了解要执行的操作。然后返回一个HTTP 303,指示客户端它已处于失效状态并刷新。
选项3:完整列表 - 如果您的设置大小合理,则每次要更新时都可以传递完整的联系人列表。这种逻辑是一种超级简单的擦除和替换。
从RESTful角度来看,真正重要的是您的应用程序在所有方法中以一致的方式运行。因此,如果MERGE意味着添加,它应该始终意味着添加。如果您希望将一组完整的ID传递给PUT,那么总是传递一套完整的ID。
控制器设计
如果是我,我会把你的控制器分成多个控制器。一个控制器处理组另一个交易联系人(作为一个组),第三个处理一个组内的一个联系人。有点像...
//api/Group/
public List<GroupModel> 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
如果您希望您的网址显示为嵌套(例如:/api/Group/Contacts
,/api/Group/Contact
),您可以查看this other post I wrote。恕我直言,asp.net的路由需要调整以支持嵌套更容易......但这是一个不同的问题; - )
答案 1 :(得分:4)
为了回应EBarr所说的,在Web API中进行分层路由可能会有点痛苦。我在我的服务中经常使用它,所以我为Web API构建了替换路由服务。 Nuget为here,来源位于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<ContactsController>());
// /api/Contact/{contactid}
router.Add("Contact", rc =>
rc.Add("{contactid}", rci => rci.To<ContactController>()));
// /api/Group/{groupid}
// /api/Group/{groupid}/Contacts
router.Add("Group", rg =>
rg.Add("{groupid}", rgi => rgi.To<GroupController>()
.Add("Contacts", rgc => rgc.To<GroupContactsController>())));
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<string, object> 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的引用并进行试用。如果您对使用这种路由可以走多远感到好奇,那么有一个更复杂的样本here。