编辑:虽然这个答案仍然适用于 Web API 1,但对于 Web API 2,我强烈建议使用 Daniel Halan's answer,因为它是映射子资源的最先进技术(以及其他细节)。
有些人不喜欢在 Web API 中使用 {action},因为他们认为这样做会破坏 REST“意识形态”……我认为。 {action} 只是一个有助于路由的结构。它在您的实现内部,与用于访问 资源 的 HTTP 动词无关。
如果您将 HTTP 动词约束放在操作上并相应地命名它们,您不会违反任何 RESTful 准则,并且最终会得到更简单、更简洁的控制器,而不是每个子资源的大量单独控制器。请记住:动作只是一种路由机制,它在您的实现内部。如果您与框架抗争,那么框架或您的实现都有问题。只需使用 HTTPMETHOD 约束映射路由就可以了:
routes.MapHttpRoute(
name: "OneLevelNested",
routeTemplate: "api/customers/{customerId}/orders/{orderId}",
constraints: new { httpMethod = new HttpMethodConstraint(new string[] { "GET" }) },
defaults: new { controller = "Customers", action = "GetOrders", orderId = RouteParameter.Optional, }
);
您可以像这样在 CustomersController 中处理这些:
public class CustomersController
{
// ...
public IEnumerable<Order> GetOrders(long customerId)
{
// returns all orders for customerId!
}
public Order GetOrders(long customerId, long orderId)
{
// return the single order identified by orderId for the customerId supplied
}
// ...
}
您还可以在同一“资源”(订单)上路由创建操作:
routes.MapHttpRoute(
name: "OneLevelNested",
routeTemplate: "api/customers/{customerId}/orders",
constraints: new { httpMethod = new HttpMethodConstraint(new string[] { "POST" }) },
defaults: new { controller = "Customers", action = "CreateOrder", }
);
并在客户控制器中进行相应处理:
public class CustomersController
{
// ...
public Order CreateOrder(long customerId)
{
// create and return the order just created (with the new order id)
}
// ...
}
是的,您仍然必须创建很多路由,因为 Web API 仍然无法根据路径路由到不同的方法……但我认为以声明方式定义路由比提出一个更简洁基于枚举或其他技巧的自定义调度机制。
对于您的 API 的使用者来说,它看起来完全是 RESTful:
GET http://your.api/customers/1/orders(映射到 GetOrders(long) 返回客户 1 的所有订单)
GET http://your.api/customers/1/orders/22(映射到 GetOrders(long, long) 为客户 1 返回订单 22
POST http://your.api/customers/1/orders(映射到 CreateOrder(long),它将创建一个订单并将其返回给调用者(使用刚刚创建的新 ID)
但不要把我的话当成绝对真理。我仍在尝试它,我认为 MS 未能正确解决子资源访问问题。
我强烈建议您尝试http://www.servicestack.net/,以减少编写 REST api 的痛苦体验...但不要误会我的意思,我喜欢 Web API 并将它用于我的大多数专业项目,主要是因为它更容易找到已经“了解”它的程序员......对于我的个人项目,我更喜欢 ServiceStack。