是否可以将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) {...}
}
答案 0 :(得分:33)
Web API(1.x-2.x)不支持在不同控制器上具有相同路径的多个属性路由。结果是404,因为所有路由都匹配多个控制器,此时Web API会认为结果不明确。
请注意,MVC Core确实支持此场景说明:MVC Core既可以作为MVC,也可以作为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<string, object> 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<string, object> values,
HttpRouteDirection routeDirection)
{
return request.Method == Method;
}
}
答案 1 :(得分:3)
您并不总是需要在控制器上指定RoutePrefix
。您可以直接将路线放在网络方法上:
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
属性一起使用。这意味着只要你没有任何相互冲突的路线(这是一个大问题),你应该没问题。
答案 2 :(得分:1)
利用局部类。 Partial Classes and Methods - C# MSDN
创建两个文件: BooksController.Write.cs 和 BooksController.Read.cs 只有RoutePrefix一个文件,因为它们是同一类,所以会给您一个错误,提示您两次为同一类添加前缀。
两个文件都将作为一个单独的类编译(因为它是一个单独的类,但是拆分为不同的文件)。
// File BooksController.Write.cs
[RoutePrefix("api/Books")]
public partial class BooksController : EventStoreApiController
{
[Route("")]
public void Post([FromBody] CommandWrapper commandWrapper){...}
}
// File BooksController.Read.cs
public partial class BooksController : MongoDbApiController
{
[Route("")]
public Book[] Get() {...}
[Route("{id:int}")]
public Book Get(int id) {...}
}