多个控制器类型具有相同的路由前缀ASP.NET Web Api

是否可以将GET和POST分离为单独的API控制器类型并使用相同的路径前缀访问它们?

这是我的控制器:

[RoutePrefix("api/Books")] public class BooksWriteController : EventStoreApiController { [Route("")] public void Post([FromBody] CommandWrapper commandWrapper){...} } [RoutePrefix("api/Books")] public class BooksReadController : MongoDbApiController { [Route("")] public Book[] Get() {...} [Route("{id:int}")] public Book Get(int id) {...} } 

Web API(1.x-2.x)不支持在不同控制器上具有相同路径的多个属性路由。 结果是404,因为所有路由都匹配多个控制器,此时Web API会认为结果不明确。

请注意, MVC Core确实支持此场景注释:MVC Core既可用作MVC,也可用作Web API。

如果选择使用Web API 2.11(或更新版本),则可以为每个控制器的http方法创建路径约束,并使用它来代替内置的路径属性。 下面的示例显示您可以使用RoutePrefix或直接路由(如kmacdonald的答案)。

 using System.Collections.Generic; using System.Net.Http; using System.Web.Http; using System.Web.Http.Routing; public class BooksWriteController : ApiController { [PostRoute("api/Books")] public void Post() { } } [RoutePrefix("api/books")] public class BooksReadController : ApiController { [GetRoute] public void Get() { } [GetRoute("{id:int}")] public void Get(int id) { } } 

这两个类简化了约束路由属性的使用

 class GetRouteAttribute : MethodConstraintedRouteAttribute { public GetRouteAttribute(string template) : base(template ?? "", HttpMethod.Get) { } } class PostRouteAttribute : MethodConstraintedRouteAttribute { public PostRouteAttribute(string template) : base(template ?? "", HttpMethod.Post) { } } 

此类允许为生成的路由添加约束

 class MethodConstraintedRouteAttribute : RouteFactoryAttribute { public MethodConstraintedRouteAttribute(string template, HttpMethod method) : base(template) { Method = method; } public HttpMethod Method { get; private set; } public override IDictionary Constraints { get { var constraints = new HttpRouteValueDictionary(); constraints.Add("method", new MethodConstraint(Method)); return constraints; } } } 

这只是一个标准的路由约束,你可能想要缓存约束对象以减少分配。

 class MethodConstraint : IHttpRouteConstraint { public HttpMethod Method { get; private set; } public MethodConstraint(HttpMethod method) { Method = method; } public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) { return request.Method == Method; } } 

您并不总是需要在控制器上指定RoutePrefix 。 您可以直接将路径放在Web方法上:

 public class BooksWriteController : EventStoreApiController { [Route("api/Books")] public void Post([FromBody] CommandWrapper commandWrapper){...} } public class BooksReadController : MongoDbApiController { [Route("api/Books")] public TaskTypeInfo[] Get() {...} [Route("api/Books/{id:int}")] public TaskTypeInfo Get(int id) {...} } 

但是,我想你的RoutePrefix可以在两个控制器上正常工作。 我认为RoutePrefix属性与实际定义路由的Route属性一起使用。 这意味着只要你没有任何冲突的路线(这是一个大问题),你应该没事。