同时使用基于属性路由和基于约定的路由与Web API和MVC

时间:2015-05-28 13:19:28

标签: c# asp.net-mvc-5 asp.net-mvc-routing asp.net-web-api2 asp.net-web-api-routing

我有一个使用基于约定的路由的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的控制器,例如,它有一个路由到/ Elements的Index()方法或一个路由到/ Elements / ListPublic的ListPublic()方法。我通过上面提到的基于约定的路由实现了这一点。

我还有一堆Web Api控制器(例如,TablesController),我想使用/ api / v0 / tables路由路由到它。我试图这样做:

[RoutePrefix("api/v0/tables")]
public class TablesController : ApiController
{
    [Route()]
    public string Get()
    {
        // ...
    }
}

正如您所看到的,它不是相同的路由模式:api调用都以api / v0 /为前缀。但出于某种原因,它似乎仍将其视为默认的{controller} / {action}路由。

2 个答案:

答案 0 :(得分:1)

发生的事情是&#34;首次注册&#34;路线正在生效。如果我将MVC路由定义为 {controller}/{action}/{id}

和定义为

的Web API路由

{controller}/{action}/{id}

第一条注册路线将生效。

为什么会这样?想象一下,您向服务器发送请求

foo/bar/1

这匹配哪条路线?

两个!

无论使用何种路由,它都会选择与路径匹配的第一个结果。如果您想了解如何使这些路线有效的示例,请查看此link

答案 1 :(得分:0)

如果URL匹配两个不同的路由模板,如果它们引用MVC或Web API并不重要,唯一的规则是第一个注册的路由将用于选择要执行的操作。

在特定情况下,如果您对此URL发出GET请求:/api/v0/tables,则操作选择器会开始检查已注册的路由。

您注册的第一条路线是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等)。因此,您可以在接受不同方法的相同路径中执行两个类似的操作,并且Action选择器将知道选择哪个,但这不能解决您的问题