多个控制器类型具有相同的路由前缀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
属性一起使用。 这意味着只要你没有任何冲突的路线(这是一个大问题),你应该没事。