/customers/1/products/10/
。我看到了一些命名不同于Get()
、Post()
等的ApiController动作的示例,例如这里,我看到一个名为GetOrder()
的操作示例。但我找不到任何文档。这是实现这一点的方法吗?/customers/1/products/10/
。我看到了一些命名不同于Get()
、Post()
等的ApiController动作的示例,例如这里,我看到一个名为GetOrder()
的操作示例。但我找不到任何文档。这是实现这一点的方法吗?很抱歉,我已经多次更新了这个问题,因为我自己正在寻找解决方案。
似乎有很多方法可以解决这个问题,但迄今为止我发现最有效的方法是:
在默认路由下添加以下内容:
routes.MapHttpRoute(
name: "OneLevelNested",
routeTemplate: "api/{controller}/{customerId}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
这个路由会匹配URL中任何控制器操作和匹配的片段名称。例如:
/api/customers/1/orders 会匹配:
public IEnumerable<Order> Orders(int customerId)
/api/customers/1/orders/123将匹配:
public Order Orders(int customerId, int id)
/api/customers/1/products将匹配:
public IEnumerable<Product> Products(int customerId)
/api/customers/1/products/123将匹配:
public Product Products(int customerId, int id)
方法名必须与路由中指定的 {action} 段匹配。
从评论中得知
自 RC 版本开始,您需要告诉每个操作可接受哪种动词,例如 [HttpGet]
等。
编辑:虽然本答案仍适用于Web API 1,但对于Web API 2,我强烈建议使用Daniel Halan的答案,因为它是映射子资源(以及其他好处)的最新技术。
有些人不喜欢在Web API中使用{action},因为他们认为这样做会破坏REST的“思想”...... 我不同意。 {action}只是一个有助于路由的构造。它是内部实现的一部分,与用于访问资源的HTTP动词无关。
如果对操作施加HTTP动词约束并相应地命名,您将不会违反任何RESTful指南,并且最终会得到更简洁的控制器,而不是每个子资源都有大量单独的控制器。记住:操作只是路由机制,它是您实现的一部分。如果你与框架作斗争,那么可能是框架或您的实现出了问题。只需使用HTTP方法约束映射路由即可:
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", }
);
在Customer控制器中相应地处理它:
public class CustomersController
{
// ...
public Order CreateOrder(long customerId)
{
// create and return the order just created (with the new order id)
}
// ...
}
GET http://your.api/customers/1/orders
(映射到返回客户1的所有订单的GetOrders(long))
GET http://your.api/customers/1/orders/22
(映射到返回客户1的订单22的GetOrders(long, long))
POST http://your.api/customers/1/orders
(映射到CreateOrder(long),该方法将创建订单并将其返回给调用者(包括新创建的ID))public class CustomersController : ApiController
{
[Route("api/customers/{id:guid}/products")]
public IEnumerable<Product> GetCustomerProducts(Guid id) {
return new Product[0];
}
}
config.MapHttpAttributeRoutes();
https://dev59.com/Smgv5IYBdhLWcg3wMN60#15341810
以下是完整的答案,因为我不确定当一篇帖子回答两个SO问题时该怎么做:(
controller = "Child"
连接起来。我有几个子控制器,不想为每个子控制器都映射一个特定的路由,反复使用controller = "ChildX"
和controller = "ChildY"
。WebApiConfig
如下所示:config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ChildApi",
routeTemplate: "api/{parentController}/{parentId}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
我的父级控制器非常标准,并且与上面的默认路由匹配。示例子控制器如下:
public class CommentController : ApiController
{
// GET api/product/5/comment
public string Get(ParentController parentController, string parentId)
{
return "This is the comment controller with parent of "
+ parentId + ", which is a " + parentController.ToString();
}
// GET api/product/5/comment/122
public string Get(ParentController parentController, string parentId,
string id)
{
return "You are looking for comment " + id + " under parent "
+ parentId + ", which is a "
+ parentController.ToString();
}
}
public enum ParentController
{
Product
}
枚举
,因此仍需要在两个独立的位置管理父控制器。它本可以很容易地成为一个字符串参数,但我想防止api/crazy-non-existent-parent/5/comment/122
发挥作用。可能有更好的解决方案,甚至更通用,但是正如我所说,这对我来说已经足够了。