具有相同路由前缀 ASP.NET Web Api 的多个控制器类型
Posted
技术标签:
【中文标题】具有相同路由前缀 ASP.NET Web Api 的多个控制器类型【英文标题】:Multiple Controller Types with same Route prefix ASP.NET Web Api 【发布时间】:2014-05-30 10:46:26 【问题描述】:是否可以将 GET 和 POST 分成不同的 API 控制器类型并使用相同的 Route Prefix 访问它们?
这是我的控制器:
[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) ...
【问题讨论】:
虽然它仍然是同一个控制器,但你可以做的一件事是让你的控制器成为一个部分类,将你的 GET 放在一个文件中,将你的 POST 放在另一个文件中。 【参考方案1】:Web API (1.x-2.x) 不支持在不同控制器上具有相同路径的多个属性路由。结果是 404,因为所有路由都匹配多个控制器,此时 Web API 将认为结果不明确。
请注意,MVC Core 确实支持这种情况说明:MVC Core 既可用作 MVC 又可用作 Web API。
如果您选择使用 Web API 2.11(或更新版本),您可以为每个控制器的 http 方法创建一个路由约束,并使用它来代替内置的路由属性。下面的示例显示您可以使用 RoutePrefix 或直接使用 Routes(如 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;
【讨论】:
@YishaiGalatzer:有什么具体原因需要在这里自定义MethodConstraint
而不是重复使用System.Web.Http.Routing.HttpMethodConstraint?
重点是限制路线本身。内置属性仅在运行太晚的动作选择中起作用。
如果有人正在查看此答案并且在命名路线时遇到问题,然后使用 Url.Link(...) 构建路线。这里有一个类似的答案:***.com/questions/40892637/… 我看到的主要区别是附加接口以及可能将 HttpMethodConstraint 与一组 HttpMethods 一起使用。无论哪种情况,另一个答案都使路由命名仍然可以供参考。【参考方案2】:
您并不总是需要在控制器上指定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
属性结合使用。这意味着只要您没有任何冲突的路线(这是一个大问题),您应该没问题。
【讨论】:
谢谢,这不是问题。问题是根据 GET 或 POST 选择正确的控制器类型【参考方案3】:利用部分类。 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) ...
【讨论】:
出现错误——部分声明不能指定不同的基类以上是关于具有相同路由前缀 ASP.NET Web Api 的多个控制器类型的主要内容,如果未能解决你的问题,请参考以下文章
.Net Core Web API,根据值创建路由逻辑到具有相同路由前缀的不同控制器
(Asp.Net MVC / Web-Api)中具有相同身份验证系统的多个项目
在 ASP.NET Web API 中使用多个 Get 方法进行路由