同时使用属性路由和基于约定的路由与Web API和MVC
本文关键字:路由 约定 MVC API Web 于约定 属性 | 更新日期: 2023-09-27 17:50:58
我有一个使用基于约定路由的Asp.net MVC web应用程序。我最近添加了一些Web Api 2控制器,为此我使用了属性路由。尽管文档声称你可以使用这两种方法,但我可以让(属性路由)API方法工作,或者(约定路由)web应用程序方法。
这是RouteConfig.RegisterRoutes():
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Tables", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "Foo.Cms.Controllers" }
);
}
这是WebApiConfig.Register():
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// The models currently only serialize succesfully to xml, so we'll remove the json formatter.
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.JsonFormatter);
}
这是Application_Start():
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
}
这样,只有到web api控制器的路由有效。如果我切换GlobalConfiguration.Register()和RouteConfig.RegisterRoutes(),如下所示:
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
…只有基于约定的路由才能工作。
我很茫然。这是怎么回事?
编辑:我想达到的目标:
应用程序当前使用基本的{controller}/{action}/parameters约定。因此,我有一个名为ElementsController的控制器,例如,它有一个Index()方法路由到/Elements或一个ListPublic()方法路由到/Elements/ListPublic。我通过上面提到的基于约定的路由实现了这一点。
我也有一堆Web Api控制器(例如,TablesController),我想路由到使用/Api/v0/tables路由。我试着这样做:
[RoutePrefix("api/v0/tables")]
public class TablesController : ApiController
{
[Route()]
public string Get()
{
// ...
}
}
正如你所看到的,它不是相同的路由模式:api调用都以api/v0/为前缀。然而,由于某些原因,它似乎仍然将它们视为默认的{controller}/{action}路由。
实际情况是"第一个注册的"路由正在生效。如果我有一个MVC路由定义为{controller}/{action}/{id}
和定义为
的Web API路由 {controller}/{action}/{id}
第一个注册的路由生效。
为什么会这样?假设您向服务器发送一个请求
foo/bar/1
这与哪条路由匹配?
两个!
它将选择第一个与路由匹配的结果,而不管使用的路由类型。如果您想要一些关于如何使这些路由工作的示例,请查看这个链接
如果一个URL匹配两个不同的路由模板,不管它们是引用MVC还是Web API,唯一的规则是第一个注册的路由将被用来选择要执行的操作。
在您的特殊情况下,如果您向此URL: /api/v0/tables
发出GET请求,则操作选择器开始检查已注册的路由。
你注册的第一个路由是这样的MVC路由:/{controller}/{action}/{id}
。因此,模板匹配以下值:
controller = "api"
action = "v0"
id = "tables"
如果你在MVC路由之前注册属性路由,第一个匹配的路由模板将是你的路由属性模板,因此Web API控制器动作将被正确选择。
换句话说,如果你需要在同一个应用程序中路由Web API和MVC,你必须为每个操作使用匹配不同url的路由,并且要注意它们注册的顺序:先注册更具体的模板,然后是更通用的模板,这样通用模板就不会吞噬应该与特定模板匹配的uri。
这不是唯一的选择。你也可以使用路由约束,例如,如果你所有的API都有一个特定的命名约定,可以用一个约束来检查(例如一个Regex约束),你仍然可以使用约定路由的Web API。
注意:除了路由匹配之外,操作还需要支持HTTP方法(如POST, GET等)。所以你可以在同一个路由中有两个接受不同方法的类似动作,而动作选择器会知道选择哪一个,但这并不能解决你的问题