Using Attribute Routing and Conventional Routing with Web API and MVC Simultaneously

I have an Asp.net MVC web application using convention based routing. I recently added a few Web Api 2 controllers for which I used attribute routing. Despite the documentation claiming that you can use both of these methods, I can either use API methods with a routing attribute or web application methods (protocol-routed).

This is 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" }
        );
    }

      

This is 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);
    }

      

And this is 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;
    }

      

So only the web controller api routing works. If I switch GlobalConfiguration.Register () and RouteConfig.RegisterRoutes () like so:

        RouteConfig.RegisterRoutes(RouteTable.Routes);
        GlobalConfiguration.Configure(WebApiConfig.Register);

      

... only legend-based routing works.

I'm at a loss. What's going on here?

Edit:

What I am trying to achieve:

The application currently uses the basic convention {controller} / {action} / parameters. So I have a controller called ElementsController that has, for example, an Index () method that redirects to / Elements, or ListPublic () that redirects to / Elements / ListPublic. I achieved this with the convention-based routing mentioned above.

I also have a bunch of Web Api controllers (like TablesController) that I want to use to route / api / v 0 / tables. I tried to achieve it like this:

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

      

As you can see, this is not the same route pattern: all api calls are prefixed with api / v0 /. However, for some reason it still seems that they are treating them as the {controller} / {action} default routes.

+3


source to share


2 answers


What happens is that the "first registered" route takes effect. If I have an MVC route defined as {controller}/{action}/{id}

and the web API route defined as

{controller}/{action}/{id}

The first registered route will take effect.

Why is this so? Imagine you are sending a request to the server



foo/bar/1

Which route matches this value?

AND!

It will select the first result that matches the route regardless of the routing type used . If you need some examples on how to make these routes check out this link

+1


source


If the URL matches two different route patterns, it doesn't matter if they refer to MVC or Web API, the only rule is that the first registered route will be used to select the action to take.

In your specific case, if you make a GET request to this url:, the /api/v0/tables

action selector starts checking the registered routes.

The first route that you register is the route MVC as follows: /{controller}/{action}/{id}

. So the pattern matches these values:

controller = "api"
action = "v0"
id = "tables"

      



If you register attribute routes prior to the MVC route, the first pattern of the matching route will be your route attribute pattern, so the web API controller action will be correctly selected.

In other words, if you need to route Web API and MVC in the same application, you need to use routes that correspond to different URLs for each action, and be careful with the order in which they are registered: register the more specific templates first, and then more general, so that common patterns do not swallow URIs that must be matched against specific patterns.

This is not the only option. You can also use route constraints, for example, if your entire API has a specific naming convention that can be checked with a constraint (such as a Regex constraint), you can still use web API routing conventions.

NOTE. Besides route matching, it is also required that the HTTP method (like POST, GET, etc.) be supported by the action. Thus, you can have two similar actions on the same route that take different methods, and the action selector will know which one to choose, but that doesn't solve your problem.

0


source







All Articles